Merge pull request #105 from atom/windows-menu
Implement menu API on Windows, fixes #75.
This commit is contained in:
commit
6a712d4db4
21 changed files with 530 additions and 200 deletions
|
@ -351,6 +351,10 @@ void Menu::Initialize(v8::Handle<v8::Object> target) {
|
||||||
|
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "popup", Popup);
|
NODE_SET_PROTOTYPE_METHOD(t, "popup", Popup);
|
||||||
|
|
||||||
|
#if defined(OS_WIN)
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "attachToWindow", AttachToWindow);
|
||||||
|
#endif
|
||||||
|
|
||||||
target->Set(v8::String::NewSymbol("Menu"), t->GetFunction());
|
target->Set(v8::String::NewSymbol("Menu"), t->GetFunction());
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
|
|
|
@ -68,7 +68,9 @@ class Menu : public EventEmitter,
|
||||||
|
|
||||||
static v8::Handle<v8::Value> Popup(const v8::Arguments &args);
|
static v8::Handle<v8::Value> Popup(const v8::Arguments &args);
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_WIN)
|
||||||
|
static v8::Handle<v8::Value> AttachToWindow(const v8::Arguments &args);
|
||||||
|
#elif defined(OS_MACOSX)
|
||||||
static v8::Handle<v8::Value> SetApplicationMenu(const v8::Arguments &args);
|
static v8::Handle<v8::Value> SetApplicationMenu(const v8::Arguments &args);
|
||||||
static v8::Handle<v8::Value> SendActionToFirstResponder(
|
static v8::Handle<v8::Value> SendActionToFirstResponder(
|
||||||
const v8::Arguments &args);
|
const v8::Arguments &args);
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
#include "browser/api/atom_api_menu_win.h"
|
#include "browser/api/atom_api_menu_win.h"
|
||||||
|
|
||||||
|
#include "browser/native_window_win.h"
|
||||||
#include "browser/ui/win/menu_2.h"
|
#include "browser/ui/win/menu_2.h"
|
||||||
|
#include "common/v8_conversions.h"
|
||||||
#include "ui/gfx/point.h"
|
#include "ui/gfx/point.h"
|
||||||
|
#include "ui/gfx/screen.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
|
@ -19,8 +22,26 @@ MenuWin::~MenuWin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuWin::Popup(NativeWindow* native_window) {
|
void MenuWin::Popup(NativeWindow* native_window) {
|
||||||
|
gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
|
||||||
menu_.reset(new atom::Menu2(model_.get()));
|
menu_.reset(new atom::Menu2(model_.get()));
|
||||||
menu_->RunContextMenuAt(gfx::Point(0, 0));
|
menu_->RunContextMenuAt(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
v8::Handle<v8::Value> Menu::AttachToWindow(const v8::Arguments& args) {
|
||||||
|
v8::HandleScope scope;
|
||||||
|
|
||||||
|
Menu* self = ObjectWrap::Unwrap<Menu>(args.This());
|
||||||
|
if (self == NULL)
|
||||||
|
return node::ThrowError("Menu is already destroyed");
|
||||||
|
|
||||||
|
NativeWindow* native_window;
|
||||||
|
if (!FromV8Arguments(args, &native_window))
|
||||||
|
return node::ThrowTypeError("Bad argument");
|
||||||
|
|
||||||
|
static_cast<NativeWindowWin*>(native_window)->SetMenu(self->model_.get());
|
||||||
|
|
||||||
|
return v8::Undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
|
@ -101,6 +101,9 @@ v8::Handle<v8::Value> Window::New(const v8::Arguments &args) {
|
||||||
|
|
||||||
new Window(args.This(), static_cast<base::DictionaryValue*>(options.get()));
|
new Window(args.This(), static_cast<base::DictionaryValue*>(options.get()));
|
||||||
|
|
||||||
|
// Give js code a chance to do initialization.
|
||||||
|
node::MakeCallback(args.This(), "_init", 0, NULL);
|
||||||
|
|
||||||
return args.This();
|
return args.This();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,15 @@ app = new Application
|
||||||
app.getHomeDir = ->
|
app.getHomeDir = ->
|
||||||
process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']
|
process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']
|
||||||
|
|
||||||
|
app.getBrowserWindows = ->
|
||||||
|
require('../../atom/objects-registry.js').getAllWindows()
|
||||||
|
|
||||||
|
app.setApplicationMenu = (menu) ->
|
||||||
|
require('menu').setApplicationMenu menu
|
||||||
|
|
||||||
|
app.getApplicationMenu = ->
|
||||||
|
require('menu').getApplicationMenu()
|
||||||
|
|
||||||
app.commandLine =
|
app.commandLine =
|
||||||
appendSwitch: bindings.appendSwitch,
|
appendSwitch: bindings.appendSwitch,
|
||||||
appendArgument: bindings.appendArgument
|
appendArgument: bindings.appendArgument
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
EventEmitter = require('events').EventEmitter
|
EventEmitter = require('events').EventEmitter
|
||||||
|
app = require 'app'
|
||||||
v8Util = process.atomBinding 'v8_util'
|
v8Util = process.atomBinding 'v8_util'
|
||||||
objectsRegistry = require '../../atom/objects-registry.js'
|
|
||||||
|
|
||||||
BrowserWindow = process.atomBinding('window').BrowserWindow
|
BrowserWindow = process.atomBinding('window').BrowserWindow
|
||||||
BrowserWindow::__proto__ = EventEmitter.prototype
|
BrowserWindow::__proto__ = EventEmitter.prototype
|
||||||
|
|
||||||
|
BrowserWindow::_init = ->
|
||||||
|
# Simulate the application menu on platforms other than OS X.
|
||||||
|
if process.platform isnt 'darwin'
|
||||||
|
menu = app.getApplicationMenu()
|
||||||
|
@setMenu menu if menu?
|
||||||
|
|
||||||
BrowserWindow::toggleDevTools = ->
|
BrowserWindow::toggleDevTools = ->
|
||||||
opened = v8Util.getHiddenValue this, 'devtoolsOpened'
|
opened = v8Util.getHiddenValue this, 'devtoolsOpened'
|
||||||
if opened
|
if opened
|
||||||
|
@ -17,12 +23,20 @@ BrowserWindow::toggleDevTools = ->
|
||||||
BrowserWindow::restart = ->
|
BrowserWindow::restart = ->
|
||||||
@loadUrl(@getUrl())
|
@loadUrl(@getUrl())
|
||||||
|
|
||||||
|
BrowserWindow::setMenu = (menu) ->
|
||||||
|
throw new Error('BrowserWindow.setMenu is only available on Windows') unless process.platform is 'win32'
|
||||||
|
|
||||||
|
throw new TypeError('Invalid menu') unless menu?.constructor?.name is 'Menu'
|
||||||
|
|
||||||
|
@menu = menu # Keep a reference of menu in case of GC.
|
||||||
|
@menu.attachToWindow this
|
||||||
|
|
||||||
BrowserWindow.getFocusedWindow = ->
|
BrowserWindow.getFocusedWindow = ->
|
||||||
windows = objectsRegistry.getAllWindows()
|
windows = app.getBrowserWindows()
|
||||||
return window for window in windows when window.isFocused()
|
return window for window in windows when window.isFocused()
|
||||||
|
|
||||||
BrowserWindow.fromProcessIdAndRoutingId = (processId, routingId) ->
|
BrowserWindow.fromProcessIdAndRoutingId = (processId, routingId) ->
|
||||||
windows = objectsRegistry.getAllWindows()
|
windows = app.getBrowserWindows()
|
||||||
return window for window in windows when window.getProcessId() == processId and
|
return window for window in windows when window.getProcessId() == processId and
|
||||||
window.getRoutingId() == routingId
|
window.getRoutingId() == routingId
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
BrowserWindow = require 'browser-window'
|
||||||
|
|
||||||
nextCommandId = 0
|
nextCommandId = 0
|
||||||
|
|
||||||
class MenuItem
|
class MenuItem
|
||||||
|
@ -26,7 +28,7 @@ class MenuItem
|
||||||
@commandId = ++nextCommandId
|
@commandId = ++nextCommandId
|
||||||
@click = =>
|
@click = =>
|
||||||
if typeof click is 'function'
|
if typeof click is 'function'
|
||||||
click.apply this, arguments
|
click this, BrowserWindow.getFocusedWindow()
|
||||||
else if typeof @selector is 'string'
|
else if typeof @selector is 'string'
|
||||||
Menu.sendActionToFirstResponder @selector
|
Menu.sendActionToFirstResponder @selector
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ EventEmitter = require('events').EventEmitter
|
||||||
IDWeakMap = require 'id-weak-map'
|
IDWeakMap = require 'id-weak-map'
|
||||||
MenuItem = require 'menu-item'
|
MenuItem = require 'menu-item'
|
||||||
|
|
||||||
|
app = require 'app'
|
||||||
bindings = process.atomBinding 'menu'
|
bindings = process.atomBinding 'menu'
|
||||||
|
|
||||||
Menu = bindings.Menu
|
Menu = bindings.Menu
|
||||||
|
@ -39,13 +40,22 @@ Menu::insert = (pos, item) ->
|
||||||
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
|
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
|
||||||
executeCommand: (commandId) =>
|
executeCommand: (commandId) =>
|
||||||
activeItem = @commandsMap[commandId]
|
activeItem = @commandsMap[commandId]
|
||||||
activeItem.click(activeItem) if activeItem?
|
activeItem.click() if activeItem?
|
||||||
@items.splice pos, 0, item
|
@items.splice pos, 0, item
|
||||||
@commandsMap[item.commandId] = item
|
@commandsMap[item.commandId] = item
|
||||||
|
|
||||||
|
applicationMenu = null
|
||||||
Menu.setApplicationMenu = (menu) ->
|
Menu.setApplicationMenu = (menu) ->
|
||||||
throw new TypeError('Invalid menu') unless menu?.constructor is Menu
|
throw new TypeError('Invalid menu') unless menu?.constructor is Menu
|
||||||
bindings.setApplicationMenu menu
|
applicationMenu = menu # Keep a reference.
|
||||||
|
|
||||||
|
if process.platform is 'darwin'
|
||||||
|
bindings.setApplicationMenu menu
|
||||||
|
else
|
||||||
|
windows = app.getBrowserWindows()
|
||||||
|
w.setMenu menu for w in windows
|
||||||
|
|
||||||
|
Menu.getApplicationMenu = -> applicationMenu
|
||||||
|
|
||||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
BrowserWindow = require 'browser-window'
|
BrowserWindow = require 'browser-window'
|
||||||
|
EventEmitter = require('events').EventEmitter
|
||||||
IDWeakMap = require 'id-weak-map'
|
IDWeakMap = require 'id-weak-map'
|
||||||
v8Util = process.atomBinding 'v8_util'
|
v8Util = process.atomBinding 'v8_util'
|
||||||
|
|
||||||
|
# Class to reference all objects.
|
||||||
class ObjectsStore
|
class ObjectsStore
|
||||||
@stores = {}
|
@stores = {}
|
||||||
|
|
||||||
|
@ -37,46 +39,57 @@ class ObjectsStore
|
||||||
key = "#{processId}_#{routingId}"
|
key = "#{processId}_#{routingId}"
|
||||||
delete @stores[key]
|
delete @stores[key]
|
||||||
|
|
||||||
# Objects in weak map will be not referenced (so we won't leak memory), and
|
class ObjectsRegistry extends EventEmitter
|
||||||
# every object created in browser will have a unique id in weak map.
|
constructor: ->
|
||||||
objectsWeakMap = new IDWeakMap
|
# Objects in weak map will be not referenced (so we won't leak memory), and
|
||||||
objectsWeakMap.add = (obj) ->
|
# every object created in browser will have a unique id in weak map.
|
||||||
id = IDWeakMap::add.call this, obj
|
@objectsWeakMap = new IDWeakMap
|
||||||
v8Util.setHiddenValue obj, 'atomId', id
|
@objectsWeakMap.add = (obj) ->
|
||||||
id
|
id = IDWeakMap::add.call this, obj
|
||||||
|
v8Util.setHiddenValue obj, 'atomId', id
|
||||||
|
id
|
||||||
|
|
||||||
windowsWeakMap = new IDWeakMap
|
# Remember all windows in the weak map.
|
||||||
|
@windowsWeakMap = new IDWeakMap
|
||||||
|
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) =>
|
||||||
|
if obj.constructor is BrowserWindow
|
||||||
|
id = @windowsWeakMap.add obj
|
||||||
|
obj.on 'destroyed', => @windowsWeakMap.remove id
|
||||||
|
|
||||||
process.on 'ATOM_BROWSER_INTERNAL_NEW', (obj) ->
|
# Register a new object, the object would be kept referenced until you release
|
||||||
# Remember all windows.
|
# it explicitly.
|
||||||
if obj.constructor is BrowserWindow
|
add: (processId, routingId, obj) ->
|
||||||
id = windowsWeakMap.add obj
|
# Some native objects may already been added to objectsWeakMap, be care not
|
||||||
obj.on 'destroyed', ->
|
# to add it twice.
|
||||||
windowsWeakMap.remove id
|
@objectsWeakMap.add obj unless v8Util.getHiddenValue obj, 'atomId'
|
||||||
|
id = v8Util.getHiddenValue obj, 'atomId'
|
||||||
|
|
||||||
exports.add = (processId, routingId, obj) ->
|
# Store and reference the object, then return the storeId which points to
|
||||||
# Some native objects may already been added to objectsWeakMap, be care not
|
# where the object is stored. The caller can later dereference the object
|
||||||
# to add it twice.
|
# with the storeId.
|
||||||
objectsWeakMap.add obj unless v8Util.getHiddenValue obj, 'atomId'
|
# We use a difference key because the same object can be referenced for
|
||||||
id = v8Util.getHiddenValue obj, 'atomId'
|
# multiple times by the same renderer view.
|
||||||
|
store = ObjectsStore.forRenderView processId, routingId
|
||||||
|
storeId = store.add obj
|
||||||
|
|
||||||
# Store and reference the object, then return the storeId which points to
|
[id, storeId]
|
||||||
# where the object is stored. The caller can later dereference the object
|
|
||||||
# with the storeId.
|
|
||||||
store = ObjectsStore.forRenderView processId, routingId
|
|
||||||
storeId = store.add obj
|
|
||||||
|
|
||||||
[id, storeId]
|
# Get an object according to its id.
|
||||||
|
get: (id) ->
|
||||||
|
@objectsWeakMap.get id
|
||||||
|
|
||||||
exports.get = (id) ->
|
# Remove an object according to its storeId.
|
||||||
objectsWeakMap.get id
|
remove: (processId, routingId, storeId) ->
|
||||||
|
ObjectsStore.forRenderView(processId, routingId).remove storeId
|
||||||
|
|
||||||
exports.getAllWindows = () ->
|
# Clear all references to objects from renderer view.
|
||||||
keys = windowsWeakMap.keys()
|
clear: (processId, routingId) ->
|
||||||
windowsWeakMap.get key for key in keys
|
@emit "release-renderer-view-#{processId}-#{routingId}"
|
||||||
|
ObjectsStore.releaseForRenderView processId, routingId
|
||||||
|
|
||||||
exports.remove = (processId, routingId, storeId) ->
|
# Return an array of all browser windows.
|
||||||
ObjectsStore.forRenderView(processId, routingId).remove storeId
|
getAllWindows: ->
|
||||||
|
keys = @windowsWeakMap.keys()
|
||||||
|
@windowsWeakMap.get key for key in keys
|
||||||
|
|
||||||
exports.clear = (processId, routingId) ->
|
module.exports = new ObjectsRegistry
|
||||||
ObjectsStore.releaseForRenderView processId, routingId
|
|
||||||
|
|
|
@ -52,9 +52,15 @@ unwrapArgs = (processId, routingId, args) ->
|
||||||
returnValue = metaToValue meta.value
|
returnValue = metaToValue meta.value
|
||||||
-> returnValue
|
-> returnValue
|
||||||
when 'function'
|
when 'function'
|
||||||
|
rendererReleased = false
|
||||||
|
objectsRegistry.once "release-renderer-view-#{processId}-#{routingId}", ->
|
||||||
|
rendererReleased = true
|
||||||
|
|
||||||
ret = ->
|
ret = ->
|
||||||
|
throw new Error('Calling a callback of released renderer view') if rendererReleased
|
||||||
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(processId, routingId, arguments)
|
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(processId, routingId, arguments)
|
||||||
v8Util.setDestructor ret, ->
|
v8Util.setDestructor ret, ->
|
||||||
|
return if rendererReleased
|
||||||
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
|
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
|
||||||
ret
|
ret
|
||||||
else throw new TypeError("Unknown type: #{meta.type}")
|
else throw new TypeError("Unknown type: #{meta.type}")
|
||||||
|
|
|
@ -31,7 +31,6 @@ app.on('finish-launching', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.on('closed', function() {
|
mainWindow.on('closed', function() {
|
||||||
console.log('closed');
|
|
||||||
mainWindow = null;
|
mainWindow = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,129 +38,167 @@ app.on('finish-launching', function() {
|
||||||
console.log('unresponsive');
|
console.log('unresponsive');
|
||||||
});
|
});
|
||||||
|
|
||||||
var template = [
|
if (process.platform == 'darwin') {
|
||||||
{
|
var template = [
|
||||||
label: 'Atom Shell',
|
{
|
||||||
submenu: [
|
label: 'Atom Shell',
|
||||||
{
|
submenu: [
|
||||||
label: 'About Atom Shell',
|
{
|
||||||
selector: 'orderFrontStandardAboutPanel:'
|
label: 'About Atom Shell',
|
||||||
},
|
selector: 'orderFrontStandardAboutPanel:'
|
||||||
{
|
},
|
||||||
type: 'separator'
|
{
|
||||||
},
|
type: 'separator'
|
||||||
{
|
},
|
||||||
label: 'Hide Atom Shell',
|
{
|
||||||
accelerator: 'Command+H',
|
label: 'Hide Atom Shell',
|
||||||
selector: 'hide:'
|
accelerator: 'Command+H',
|
||||||
},
|
selector: 'hide:'
|
||||||
{
|
},
|
||||||
label: 'Hide Others',
|
{
|
||||||
accelerator: 'Command+Shift+H',
|
label: 'Hide Others',
|
||||||
selector: 'hideOtherApplications:'
|
accelerator: 'Command+Shift+H',
|
||||||
},
|
selector: 'hideOtherApplications:'
|
||||||
{
|
},
|
||||||
label: 'Show All',
|
{
|
||||||
selector: 'unhideAllApplications:'
|
label: 'Show All',
|
||||||
},
|
selector: 'unhideAllApplications:'
|
||||||
{
|
},
|
||||||
type: 'separator'
|
{
|
||||||
},
|
type: 'separator'
|
||||||
{
|
},
|
||||||
label: 'Quit',
|
{
|
||||||
accelerator: 'Command+Q',
|
label: 'Quit',
|
||||||
click: function() { app.quit(); }
|
accelerator: 'Command+Q',
|
||||||
},
|
click: function() { app.quit(); }
|
||||||
]
|
},
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
label: 'Edit',
|
{
|
||||||
submenu: [
|
label: 'Edit',
|
||||||
{
|
submenu: [
|
||||||
label: 'Undo',
|
{
|
||||||
accelerator: 'Command+Z',
|
label: 'Undo',
|
||||||
selector: 'undo:'
|
accelerator: 'Command+Z',
|
||||||
},
|
selector: 'undo:'
|
||||||
{
|
},
|
||||||
label: 'Redo',
|
{
|
||||||
accelerator: 'Shift+Command+Z',
|
label: 'Redo',
|
||||||
selector: 'redo:'
|
accelerator: 'Shift+Command+Z',
|
||||||
},
|
selector: 'redo:'
|
||||||
{
|
},
|
||||||
type: 'separator'
|
{
|
||||||
},
|
type: 'separator'
|
||||||
{
|
},
|
||||||
label: 'Cut',
|
{
|
||||||
accelerator: 'Command+X',
|
label: 'Cut',
|
||||||
selector: 'cut:'
|
accelerator: 'Command+X',
|
||||||
},
|
selector: 'cut:'
|
||||||
{
|
},
|
||||||
label: 'Copy',
|
{
|
||||||
accelerator: 'Command+C',
|
label: 'Copy',
|
||||||
selector: 'copy:'
|
accelerator: 'Command+C',
|
||||||
},
|
selector: 'copy:'
|
||||||
{
|
},
|
||||||
label: 'Paste',
|
{
|
||||||
accelerator: 'Command+V',
|
label: 'Paste',
|
||||||
selector: 'paste:'
|
accelerator: 'Command+V',
|
||||||
},
|
selector: 'paste:'
|
||||||
{
|
},
|
||||||
label: 'Select All',
|
{
|
||||||
accelerator: 'Command+A',
|
label: 'Select All',
|
||||||
selector: 'selectAll:'
|
accelerator: 'Command+A',
|
||||||
},
|
selector: 'selectAll:'
|
||||||
]
|
},
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
label: 'View',
|
{
|
||||||
submenu: [
|
label: 'View',
|
||||||
{
|
submenu: [
|
||||||
label: 'Reload',
|
{
|
||||||
accelerator: 'Command+R',
|
label: 'Reload',
|
||||||
click: function() { BrowserWindow.getFocusedWindow().restart(); }
|
accelerator: 'Command+R',
|
||||||
},
|
click: function() { BrowserWindow.getFocusedWindow().restart(); }
|
||||||
{
|
},
|
||||||
label: 'Enter Fullscreen',
|
{
|
||||||
click: function() { BrowserWindow.getFocusedWindow().setFullscreen(true); }
|
label: 'Enter Fullscreen',
|
||||||
},
|
click: function() { BrowserWindow.getFocusedWindow().setFullscreen(true); }
|
||||||
{
|
},
|
||||||
label: 'Toggle DevTools',
|
{
|
||||||
accelerator: 'Alt+Command+I',
|
label: 'Toggle DevTools',
|
||||||
click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
|
accelerator: 'Alt+Command+I',
|
||||||
},
|
click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
|
||||||
]
|
},
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
label: 'Window',
|
{
|
||||||
submenu: [
|
label: 'Window',
|
||||||
{
|
submenu: [
|
||||||
label: 'Minimize',
|
{
|
||||||
accelerator: 'Command+M',
|
label: 'Minimize',
|
||||||
selector: 'performMiniaturize:'
|
accelerator: 'Command+M',
|
||||||
},
|
selector: 'performMiniaturize:'
|
||||||
{
|
},
|
||||||
label: 'Close',
|
{
|
||||||
accelerator: 'Command+W',
|
label: 'Close',
|
||||||
selector: 'performClose:'
|
accelerator: 'Command+W',
|
||||||
},
|
selector: 'performClose:'
|
||||||
{
|
},
|
||||||
type: 'separator'
|
{
|
||||||
},
|
type: 'separator'
|
||||||
{
|
},
|
||||||
label: 'Bring All to Front',
|
{
|
||||||
selector: 'arrangeInFront:'
|
label: 'Bring All to Front',
|
||||||
},
|
selector: 'arrangeInFront:'
|
||||||
]
|
},
|
||||||
},
|
]
|
||||||
];
|
},
|
||||||
|
];
|
||||||
|
|
||||||
menu = Menu.buildFromTemplate(template);
|
menu = Menu.buildFromTemplate(template);
|
||||||
|
|
||||||
if (process.platform == 'darwin')
|
|
||||||
Menu.setApplicationMenu(menu);
|
Menu.setApplicationMenu(menu);
|
||||||
|
} else {
|
||||||
|
var template = [
|
||||||
|
{
|
||||||
|
label: 'File',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Open',
|
||||||
|
accelerator: 'Command+O',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Close',
|
||||||
|
accelerator: 'Command+W',
|
||||||
|
click: function() { mainWindow.close(); }
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'View',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Reload',
|
||||||
|
accelerator: 'Command+R',
|
||||||
|
click: function() { mainWindow.restart(); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Enter Fullscreen',
|
||||||
|
click: function() { mainWindow.setFullscreen(true); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Toggle DevTools',
|
||||||
|
accelerator: 'Alt+Command+I',
|
||||||
|
click: function() { mainWindow.toggleDevTools(); }
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
menu = Menu.buildFromTemplate(template);
|
||||||
|
mainWindow.setMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
ipc.on('message', function(processId, routingId, type) {
|
ipc.on('message', function(processId, routingId, type) {
|
||||||
console.log(type);
|
|
||||||
if (type == 'menu')
|
if (type == 'menu')
|
||||||
menu.popup(mainWindow);
|
menu.popup(mainWindow);
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,8 +4,12 @@
|
||||||
|
|
||||||
#include "browser/native_window_win.h"
|
#include "browser/native_window_win.h"
|
||||||
|
|
||||||
|
#include "base/stl_util.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "base/values.h"
|
#include "base/values.h"
|
||||||
|
#include "browser/api/atom_api_menu.h"
|
||||||
|
#include "browser/ui/win/menu_2.h"
|
||||||
|
#include "browser/ui/win/native_menu_win.h"
|
||||||
#include "common/draggable_region.h"
|
#include "common/draggable_region.h"
|
||||||
#include "common/options_switches.h"
|
#include "common/options_switches.h"
|
||||||
#include "content/public/browser/native_web_keyboard_event.h"
|
#include "content/public/browser/native_web_keyboard_event.h"
|
||||||
|
@ -14,8 +18,10 @@
|
||||||
#include "content/public/browser/web_contents.h"
|
#include "content/public/browser/web_contents.h"
|
||||||
#include "content/public/browser/web_contents_view.h"
|
#include "content/public/browser/web_contents_view.h"
|
||||||
#include "ui/gfx/path.h"
|
#include "ui/gfx/path.h"
|
||||||
|
#include "ui/base/models/simple_menu_model.h"
|
||||||
#include "ui/views/controls/webview/webview.h"
|
#include "ui/views/controls/webview/webview.h"
|
||||||
#include "ui/views/widget/widget.h"
|
#include "ui/views/widget/widget.h"
|
||||||
|
#include "ui/views/widget/native_widget_win.h"
|
||||||
#include "ui/views/window/client_view.h"
|
#include "ui/views/window/client_view.h"
|
||||||
#include "ui/views/window/native_frame_view.h"
|
#include "ui/views/window/native_frame_view.h"
|
||||||
|
|
||||||
|
@ -26,24 +32,49 @@ namespace {
|
||||||
const int kResizeInsideBoundsSize = 5;
|
const int kResizeInsideBoundsSize = 5;
|
||||||
const int kResizeAreaCornerSize = 16;
|
const int kResizeAreaCornerSize = 16;
|
||||||
|
|
||||||
|
// Wrapper of NativeWidgetWin to handle WM_MENUCOMMAND messages, which are
|
||||||
|
// triggered by window menus.
|
||||||
|
class MenuCommandNativeWidget : public views::NativeWidgetWin {
|
||||||
|
public:
|
||||||
|
explicit MenuCommandNativeWidget(NativeWindowWin* delegate)
|
||||||
|
: views::NativeWidgetWin(delegate->window()),
|
||||||
|
delegate_(delegate) {}
|
||||||
|
virtual ~MenuCommandNativeWidget() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool PreHandleMSG(UINT message,
|
||||||
|
WPARAM w_param,
|
||||||
|
LPARAM l_param,
|
||||||
|
LRESULT* result) OVERRIDE {
|
||||||
|
if (message == WM_MENUCOMMAND) {
|
||||||
|
delegate_->OnMenuCommand(w_param, reinterpret_cast<HMENU>(l_param));
|
||||||
|
*result = 0;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NativeWindowWin* delegate_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(MenuCommandNativeWidget);
|
||||||
|
};
|
||||||
|
|
||||||
class NativeWindowClientView : public views::ClientView {
|
class NativeWindowClientView : public views::ClientView {
|
||||||
public:
|
public:
|
||||||
NativeWindowClientView(views::Widget* widget,
|
NativeWindowClientView(views::Widget* widget,
|
||||||
views::View* contents_view,
|
NativeWindowWin* contents_view)
|
||||||
NativeWindowWin* shell)
|
: views::ClientView(widget, contents_view) {
|
||||||
: views::ClientView(widget, contents_view),
|
|
||||||
shell_(shell) {
|
|
||||||
}
|
}
|
||||||
virtual ~NativeWindowClientView() {}
|
virtual ~NativeWindowClientView() {}
|
||||||
|
|
||||||
virtual bool CanClose() OVERRIDE {
|
virtual bool CanClose() OVERRIDE {
|
||||||
shell_->CloseWebContents();
|
static_cast<NativeWindowWin*>(contents_view())->CloseWebContents();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NativeWindowWin* shell_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(NativeWindowClientView);
|
DISALLOW_COPY_AND_ASSIGN(NativeWindowClientView);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,6 +204,7 @@ NativeWindowWin::NativeWindowWin(content::WebContents* web_contents,
|
||||||
resizable_(true) {
|
resizable_(true) {
|
||||||
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
|
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
|
||||||
params.delegate = this;
|
params.delegate = this;
|
||||||
|
params.native_widget = new MenuCommandNativeWidget(this);
|
||||||
params.remove_standard_frame = !has_frame_;
|
params.remove_standard_frame = !has_frame_;
|
||||||
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
||||||
window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE);
|
window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_NATIVE);
|
||||||
|
@ -329,6 +361,17 @@ gfx::NativeWindow NativeWindowWin::GetNativeWindow() {
|
||||||
return window_->GetNativeView();
|
return window_->GetNativeView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::OnMenuCommand(int position, HMENU menu) {
|
||||||
|
DCHECK(menu_);
|
||||||
|
menu_->wrapper()->OnMenuCommand(position, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::SetMenu(ui::MenuModel* menu_model) {
|
||||||
|
menu_.reset(new atom::Menu2(menu_model, true));
|
||||||
|
::SetMenu(GetNativeWindow(), menu_->GetNativeMenu());
|
||||||
|
RegisterAccelerators();
|
||||||
|
}
|
||||||
|
|
||||||
void NativeWindowWin::UpdateDraggableRegions(
|
void NativeWindowWin::UpdateDraggableRegions(
|
||||||
const std::vector<DraggableRegion>& regions) {
|
const std::vector<DraggableRegion>& regions) {
|
||||||
if (has_frame_)
|
if (has_frame_)
|
||||||
|
@ -356,12 +399,54 @@ void NativeWindowWin::UpdateDraggableRegions(
|
||||||
void NativeWindowWin::HandleKeyboardEvent(
|
void NativeWindowWin::HandleKeyboardEvent(
|
||||||
content::WebContents*,
|
content::WebContents*,
|
||||||
const content::NativeWebKeyboardEvent& event) {
|
const content::NativeWebKeyboardEvent& event) {
|
||||||
|
if (event.type == WebKit::WebInputEvent::KeyUp) {
|
||||||
|
ui::Accelerator accelerator(
|
||||||
|
static_cast<ui::KeyboardCode>(event.windowsKeyCode),
|
||||||
|
content::GetModifiersFromNativeWebKeyboardEvent(event));
|
||||||
|
|
||||||
|
if (GetFocusManager()->ProcessAccelerator(accelerator)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Any unhandled keyboard/character messages should be defproced.
|
// Any unhandled keyboard/character messages should be defproced.
|
||||||
// This allows stuff like F10, etc to work correctly.
|
// This allows stuff like F10, etc to work correctly.
|
||||||
DefWindowProc(event.os_event.hwnd, event.os_event.message,
|
DefWindowProc(event.os_event.hwnd, event.os_event.message,
|
||||||
event.os_event.wParam, event.os_event.lParam);
|
event.os_event.wParam, event.os_event.lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::Layout() {
|
||||||
|
DCHECK(web_view_);
|
||||||
|
web_view_->SetBounds(0, 0, width(), height());
|
||||||
|
OnViewWasResized();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::ViewHierarchyChanged(bool is_add,
|
||||||
|
views::View* parent,
|
||||||
|
views::View* child) {
|
||||||
|
if (is_add && child == this)
|
||||||
|
AddChildView(web_view_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NativeWindowWin::AcceleratorPressed(
|
||||||
|
const ui::Accelerator& accelerator) {
|
||||||
|
if (ContainsKey(accelerator_table_, accelerator)) {
|
||||||
|
const MenuItem& item = accelerator_table_[accelerator];
|
||||||
|
item.model->ActivatedAt(item.position);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::DeleteDelegate() {
|
||||||
|
// Do nothing, window is managed by users.
|
||||||
|
}
|
||||||
|
|
||||||
|
views::View* NativeWindowWin::GetInitiallyFocusedView() {
|
||||||
|
return web_view_;
|
||||||
|
}
|
||||||
|
|
||||||
bool NativeWindowWin::CanResize() const {
|
bool NativeWindowWin::CanResize() const {
|
||||||
return resizable_;
|
return resizable_;
|
||||||
}
|
}
|
||||||
|
@ -391,7 +476,7 @@ const views::Widget* NativeWindowWin::GetWidget() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
views::ClientView* NativeWindowWin::CreateClientView(views::Widget* widget) {
|
views::ClientView* NativeWindowWin::CreateClientView(views::Widget* widget) {
|
||||||
return new NativeWindowClientView(widget, web_view_, this);
|
return new NativeWindowClientView(widget, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView(
|
views::NonClientFrameView* NativeWindowWin::CreateNonClientFrameView(
|
||||||
|
@ -432,6 +517,44 @@ void NativeWindowWin::OnViewWasResized() {
|
||||||
web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn);
|
web_contents->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::RegisterAccelerators() {
|
||||||
|
views::FocusManager* focus_manager = GetFocusManager();
|
||||||
|
accelerator_table_.clear();
|
||||||
|
focus_manager->UnregisterAccelerators(this);
|
||||||
|
|
||||||
|
GenerateAcceleratorTable();
|
||||||
|
for (AcceleratorTable::const_iterator iter = accelerator_table_.begin();
|
||||||
|
iter != accelerator_table_.end(); ++iter) {
|
||||||
|
focus_manager->RegisterAccelerator(
|
||||||
|
iter->first, ui::AcceleratorManager::kNormalPriority, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::GenerateAcceleratorTable() {
|
||||||
|
DCHECK(menu_);
|
||||||
|
ui::SimpleMenuModel* model = static_cast<ui::SimpleMenuModel*>(
|
||||||
|
menu_->model());
|
||||||
|
FillAcceleratorTable(&accelerator_table_, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeWindowWin::FillAcceleratorTable(AcceleratorTable* table,
|
||||||
|
ui::MenuModel* model) {
|
||||||
|
int count = model->GetItemCount();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
ui::MenuModel::ItemType type = model->GetTypeAt(i);
|
||||||
|
if (type == ui::MenuModel::TYPE_SUBMENU) {
|
||||||
|
ui::MenuModel* submodel = model->GetSubmenuModelAt(i);
|
||||||
|
FillAcceleratorTable(table, submodel);
|
||||||
|
} else {
|
||||||
|
ui::Accelerator accelerator;
|
||||||
|
if (model->GetAcceleratorAt(i, &accelerator)) {
|
||||||
|
MenuItem item = { i, model };
|
||||||
|
(*table)[accelerator] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
NativeWindow* NativeWindow::Create(content::WebContents* web_contents,
|
NativeWindow* NativeWindow::Create(content::WebContents* web_contents,
|
||||||
base::DictionaryValue* options) {
|
base::DictionaryValue* options) {
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
#include "ui/gfx/size.h"
|
#include "ui/gfx/size.h"
|
||||||
#include "ui/views/widget/widget_delegate.h"
|
#include "ui/views/widget/widget_delegate.h"
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
class MenuModel;
|
||||||
|
}
|
||||||
|
|
||||||
namespace views {
|
namespace views {
|
||||||
class WebView;
|
class WebView;
|
||||||
class Widget;
|
class Widget;
|
||||||
|
@ -19,8 +23,10 @@ class Widget;
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
|
class Menu2;
|
||||||
|
|
||||||
class NativeWindowWin : public NativeWindow,
|
class NativeWindowWin : public NativeWindow,
|
||||||
public views::WidgetDelegate {
|
public views::WidgetDelegateView {
|
||||||
public:
|
public:
|
||||||
explicit NativeWindowWin(content::WebContents* web_contents,
|
explicit NativeWindowWin(content::WebContents* web_contents,
|
||||||
base::DictionaryValue* options);
|
base::DictionaryValue* options);
|
||||||
|
@ -61,6 +67,12 @@ class NativeWindowWin : public NativeWindow,
|
||||||
virtual bool IsKiosk() OVERRIDE;
|
virtual bool IsKiosk() OVERRIDE;
|
||||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||||
|
|
||||||
|
void OnMenuCommand(int position, HMENU menu);
|
||||||
|
|
||||||
|
// Set the native window menu.
|
||||||
|
void SetMenu(ui::MenuModel* menu_model);
|
||||||
|
|
||||||
|
views::Widget* window() const { return window_.get(); }
|
||||||
SkRegion* draggable_region() { return draggable_region_.get(); }
|
SkRegion* draggable_region() { return draggable_region_.get(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -72,7 +84,16 @@ class NativeWindowWin : public NativeWindow,
|
||||||
content::WebContents*,
|
content::WebContents*,
|
||||||
const content::NativeWebKeyboardEvent&) OVERRIDE;
|
const content::NativeWebKeyboardEvent&) OVERRIDE;
|
||||||
|
|
||||||
|
// Overridden from views::View:
|
||||||
|
virtual void Layout() OVERRIDE;
|
||||||
|
virtual void ViewHierarchyChanged(bool is_add,
|
||||||
|
views::View* parent,
|
||||||
|
views::View* child) OVERRIDE;
|
||||||
|
virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
|
||||||
|
|
||||||
// Overridden from views::WidgetDelegate:
|
// Overridden from views::WidgetDelegate:
|
||||||
|
virtual void DeleteDelegate() OVERRIDE;
|
||||||
|
virtual views::View* GetInitiallyFocusedView() OVERRIDE;
|
||||||
virtual bool CanResize() const OVERRIDE;
|
virtual bool CanResize() const OVERRIDE;
|
||||||
virtual bool CanMaximize() const OVERRIDE;
|
virtual bool CanMaximize() const OVERRIDE;
|
||||||
virtual string16 GetWindowTitle() const OVERRIDE;
|
virtual string16 GetWindowTitle() const OVERRIDE;
|
||||||
|
@ -85,11 +106,30 @@ class NativeWindowWin : public NativeWindow,
|
||||||
views::Widget* widget) OVERRIDE;
|
views::Widget* widget) OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
typedef struct { int position; ui::MenuModel* model; } MenuItem;
|
||||||
|
typedef std::map<ui::Accelerator, MenuItem> AcceleratorTable;
|
||||||
|
|
||||||
void OnViewWasResized();
|
void OnViewWasResized();
|
||||||
|
|
||||||
|
// Register accelerators supported by the menu model.
|
||||||
|
void RegisterAccelerators();
|
||||||
|
|
||||||
|
// Generate a table that contains memu model's accelerators and command ids.
|
||||||
|
void GenerateAcceleratorTable();
|
||||||
|
|
||||||
|
// Helper to fill the accelerator table from the model.
|
||||||
|
void FillAcceleratorTable(AcceleratorTable* table,
|
||||||
|
ui::MenuModel* model);
|
||||||
|
|
||||||
scoped_ptr<views::Widget> window_;
|
scoped_ptr<views::Widget> window_;
|
||||||
views::WebView* web_view_; // managed by window_.
|
views::WebView* web_view_; // managed by window_.
|
||||||
|
|
||||||
|
// The window menu.
|
||||||
|
scoped_ptr<atom::Menu2> menu_;
|
||||||
|
|
||||||
|
// Map from accelerator to menu item's command id.
|
||||||
|
AcceleratorTable accelerator_table_;
|
||||||
|
|
||||||
scoped_ptr<SkRegion> draggable_region_;
|
scoped_ptr<SkRegion> draggable_region_;
|
||||||
|
|
||||||
bool resizable_;
|
bool resizable_;
|
||||||
|
|
|
@ -15,10 +15,9 @@ namespace accelerator_util {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// For Mac, we convert "Ctrl" to "Command" and "MacCtrl" to "Ctrl". Other
|
// Convert "Command" to "Ctrl" on non-Mac
|
||||||
// platforms leave the shortcut untouched.
|
|
||||||
std::string NormalizeShortcutSuggestion(const std::string& suggestion) {
|
std::string NormalizeShortcutSuggestion(const std::string& suggestion) {
|
||||||
#if !defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
return suggestion;
|
return suggestion;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -26,9 +25,7 @@ std::string NormalizeShortcutSuggestion(const std::string& suggestion) {
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
base::SplitString(suggestion, '+', &tokens);
|
base::SplitString(suggestion, '+', &tokens);
|
||||||
for (size_t i = 0; i < tokens.size(); i++) {
|
for (size_t i = 0; i < tokens.size(); i++) {
|
||||||
if (tokens[i] == "Ctrl")
|
if (tokens[i] == "Command")
|
||||||
tokens[i] = "Command";
|
|
||||||
else if (tokens[i] == "MacCtrl")
|
|
||||||
tokens[i] = "Ctrl";
|
tokens[i] = "Ctrl";
|
||||||
}
|
}
|
||||||
return JoinString(tokens, '+');
|
return JoinString(tokens, '+');
|
||||||
|
|
|
@ -77,6 +77,7 @@ class Menu2 {
|
||||||
|
|
||||||
// Accessors.
|
// Accessors.
|
||||||
ui::MenuModel* model() const { return model_; }
|
ui::MenuModel* model() const { return model_; }
|
||||||
|
NativeMenuWin* wrapper() const { return wrapper_.get(); }
|
||||||
|
|
||||||
// Sets the minimum width of the menu.
|
// Sets the minimum width of the menu.
|
||||||
void SetMinimumWidth(int width);
|
void SetMinimumWidth(int width);
|
||||||
|
|
|
@ -92,6 +92,20 @@ class NativeMenuWin::MenuHostWindow {
|
||||||
DestroyWindow(hwnd_);
|
DestroyWindow(hwnd_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when the user selects a specific item.
|
||||||
|
void OnMenuCommand(int position, HMENU menu) {
|
||||||
|
NativeMenuWin* menu_win = GetNativeMenuWinFromHMENU(menu);
|
||||||
|
ui::MenuModel* model = menu_win->model_;
|
||||||
|
NativeMenuWin* root_menu = menu_win;
|
||||||
|
while (root_menu->parent_)
|
||||||
|
root_menu = root_menu->parent_;
|
||||||
|
|
||||||
|
// Only notify the model if it didn't already send out notification.
|
||||||
|
// See comment in MenuMessageHook for details.
|
||||||
|
if (root_menu->menu_action_ == MENU_ACTION_NONE)
|
||||||
|
model->ActivatedAt(position);
|
||||||
|
}
|
||||||
|
|
||||||
HWND hwnd() const { return hwnd_; }
|
HWND hwnd() const { return hwnd_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -146,20 +160,6 @@ class NativeMenuWin::MenuHostWindow {
|
||||||
return reinterpret_cast<NativeMenuWin::ItemData*>(item_data);
|
return reinterpret_cast<NativeMenuWin::ItemData*>(item_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the user selects a specific item.
|
|
||||||
void OnMenuCommand(int position, HMENU menu) {
|
|
||||||
NativeMenuWin* menu_win = GetNativeMenuWinFromHMENU(menu);
|
|
||||||
ui::MenuModel* model = menu_win->model_;
|
|
||||||
NativeMenuWin* root_menu = menu_win;
|
|
||||||
while (root_menu->parent_)
|
|
||||||
root_menu = root_menu->parent_;
|
|
||||||
|
|
||||||
// Only notify the model if it didn't already send out notification.
|
|
||||||
// See comment in MenuMessageHook for details.
|
|
||||||
if (root_menu->menu_action_ == MENU_ACTION_NONE)
|
|
||||||
model->ActivatedAt(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called as the user moves their mouse or arrows through the contents of the
|
// Called as the user moves their mouse or arrows through the contents of the
|
||||||
// menu.
|
// menu.
|
||||||
void OnMenuSelect(WPARAM w_param, HMENU menu) {
|
void OnMenuSelect(WPARAM w_param, HMENU menu) {
|
||||||
|
@ -529,6 +529,10 @@ void NativeMenuWin::SetMinimumWidth(int width) {
|
||||||
NOTIMPLEMENTED();
|
NOTIMPLEMENTED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NativeMenuWin::OnMenuCommand(int position, HMENU menu) {
|
||||||
|
host_window_->OnMenuCommand(position, menu);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// NativeMenuWin, private:
|
// NativeMenuWin, private:
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,9 @@ class NativeMenuWin {
|
||||||
void RemoveMenuListener(views::MenuListener* listener);
|
void RemoveMenuListener(views::MenuListener* listener);
|
||||||
void SetMinimumWidth(int width);
|
void SetMinimumWidth(int width);
|
||||||
|
|
||||||
|
// Called by user to generate a menu command event.
|
||||||
|
void OnMenuCommand(int position, HMENU menu);
|
||||||
|
|
||||||
// Flag to create a window menu instead of popup menu.
|
// Flag to create a window menu instead of popup menu.
|
||||||
void set_create_as_window_menu(bool flag) { create_as_window_menu_ = flag; }
|
void set_create_as_window_menu(bool flag) { create_as_window_menu_ = flag; }
|
||||||
bool create_as_window_menu() const { return create_as_window_menu_; }
|
bool create_as_window_menu() const { return create_as_window_menu_; }
|
||||||
|
|
|
@ -79,12 +79,16 @@ code will not run.
|
||||||
|
|
||||||
Returns the version of current bundle or executable.
|
Returns the version of current bundle or executable.
|
||||||
|
|
||||||
|
## app.getBrowserWindows()
|
||||||
|
|
||||||
|
Returns an array of all browser windows.
|
||||||
|
|
||||||
## app.commandLine.appendSwitch(switch, [value])
|
## app.commandLine.appendSwitch(switch, [value])
|
||||||
|
|
||||||
Append a switch [with optional value] to Chromium's command line.
|
Append a switch [with optional value] to Chromium's command line.
|
||||||
|
|
||||||
**Note:** This will not affect `process.argv`, and is mainly used by
|
**Note:** This will not affect `process.argv`, and is mainly used by developers
|
||||||
**developers to control some low-level Chromium behaviors.
|
to control some low-level Chromium behaviors.
|
||||||
|
|
||||||
## app.commandLine.appendArgument(value)
|
## app.commandLine.appendArgument(value)
|
||||||
|
|
||||||
|
|
|
@ -23,5 +23,5 @@
|
||||||
|
|
||||||
## Notes on accelerator
|
## Notes on accelerator
|
||||||
|
|
||||||
On OS X, the `Ctrl` would automatically translated to `Command`, if you really
|
On Linux and Windows, the `Command` would be translated to `Ctrl`, so usually
|
||||||
want `Ctrl` on OS X, you should use `MacCtrl`.
|
you can use `Command` for most of the commands.
|
||||||
|
|
40
spec/main.js
40
spec/main.js
|
@ -1,6 +1,7 @@
|
||||||
var app = require('app');
|
var app = require('app');
|
||||||
var ipc = require('ipc');
|
var ipc = require('ipc');
|
||||||
var BrowserWindow = require('browser-window');
|
var BrowserWindow = require('browser-window');
|
||||||
|
var Menu = require('menu');
|
||||||
|
|
||||||
var window = null;
|
var window = null;
|
||||||
|
|
||||||
|
@ -39,6 +40,45 @@ app.on('window-all-closed', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('finish-launching', function() {
|
app.on('finish-launching', function() {
|
||||||
|
var template = [
|
||||||
|
{
|
||||||
|
label: 'File',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Open',
|
||||||
|
accelerator: 'Command+O',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Close',
|
||||||
|
accelerator: 'Command+W',
|
||||||
|
click: function(item, window) { window.close(); }
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'View',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Reload',
|
||||||
|
accelerator: 'Command+R',
|
||||||
|
click: function(item, window) { window.restart(); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Enter Fullscreen',
|
||||||
|
click: function(item, window) { window.setFullScreen(true); }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Toggle DevTools',
|
||||||
|
accelerator: 'Alt+Command+I',
|
||||||
|
click: function(item, window) { window.toggleDevTools(); }
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
var menu = Menu.buildFromTemplate(template);
|
||||||
|
app.setApplicationMenu(menu);
|
||||||
|
|
||||||
// Test if using protocol module would crash.
|
// Test if using protocol module would crash.
|
||||||
require('protocol').registerProtocol('test-if-crashes', function() {});
|
require('protocol').registerProtocol('test-if-crashes', function() {});
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
describe 'http', ->
|
describe 'http', ->
|
||||||
describe 'sending request of http protocol urls', ->
|
describe 'sending request of http protocol urls', ->
|
||||||
it 'should not crash', (done) ->
|
it 'should not crash', ->
|
||||||
$.ajax
|
$.get 'https://api.github.com/zen'
|
||||||
url: 'http://127.0.0.1'
|
|
||||||
success: -> done()
|
|
||||||
error: -> done()
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue