diff --git a/atom/browser/api/atom_api_box_layout.cc b/atom/browser/api/atom_api_box_layout.cc new file mode 100644 index 000000000000..99e2efc55b75 --- /dev/null +++ b/atom/browser/api/atom_api_box_layout.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_box_layout.h" + +#include + +#include "atom/browser/api/atom_api_view.h" +#include "atom/common/api/constructor.h" +#include "native_mate/dictionary.h" + +#include "atom/common/node_includes.h" + +namespace mate { + +template <> +struct Converter { + static bool FromV8(v8::Isolate* isolate, + v8::Handle val, + views::BoxLayout::Orientation* out) { + std::string orientation; + if (!ConvertFromV8(isolate, val, &orientation)) + return false; + if (orientation == "horizontal") + *out = views::BoxLayout::kHorizontal; + else if (orientation == "vertical") + *out = views::BoxLayout::kVertical; + else + return false; + return true; + } +}; + +} // namespace mate + +namespace atom { + +namespace api { + +BoxLayout::BoxLayout(views::BoxLayout::Orientation orientation) + : LayoutManager(new views::BoxLayout(orientation)) {} + +BoxLayout::~BoxLayout() {} + +void BoxLayout::SetFlexForView(mate::Handle view, int flex) { + auto* box_layout = static_cast(layout_manager()); + box_layout->SetFlexForView(view->view(), flex); +} + +// static +mate::WrappableBase* BoxLayout::New(mate::Arguments* args, + views::BoxLayout::Orientation orientation) { + auto* layout = new BoxLayout(orientation); + layout->InitWith(args->isolate(), args->GetThis()); + return layout; +} + +// static +void BoxLayout::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + prototype->SetClassName(mate::StringToV8(isolate, "BoxLayout")); + mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) + .SetMethod("setFlexForView", &BoxLayout::SetFlexForView); +} + +} // namespace api + +} // namespace atom + +namespace { + +using atom::api::BoxLayout; + +void Initialize(v8::Local exports, + v8::Local unused, + v8::Local context, + void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.Set("BoxLayout", mate::CreateConstructor( + isolate, base::Bind(&BoxLayout::New))); +} + +} // namespace + +NODE_BUILTIN_MODULE_CONTEXT_AWARE(atom_browser_box_layout, Initialize) diff --git a/atom/browser/api/atom_api_box_layout.h b/atom/browser/api/atom_api_box_layout.h new file mode 100644 index 000000000000..b29e2eda6ab0 --- /dev/null +++ b/atom/browser/api/atom_api_box_layout.h @@ -0,0 +1,40 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_BOX_LAYOUT_H_ +#define ATOM_BROWSER_API_ATOM_API_BOX_LAYOUT_H_ + +#include "atom/browser/api/atom_api_layout_manager.h" +#include "native_mate/handle.h" +#include "ui/views/layout/box_layout.h" + +namespace atom { + +namespace api { + +class View; + +class BoxLayout : public LayoutManager { + public: + static mate::WrappableBase* New(mate::Arguments* args, + views::BoxLayout::Orientation orientation); + + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + void SetFlexForView(mate::Handle view, int flex); + + protected: + explicit BoxLayout(views::BoxLayout::Orientation orientation); + ~BoxLayout() override; + + private: + DISALLOW_COPY_AND_ASSIGN(BoxLayout); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_BOX_LAYOUT_H_ diff --git a/atom/browser/api/atom_api_browser_window.cc b/atom/browser/api/atom_api_browser_window.cc index ce85e52d4370..a987828be55b 100644 --- a/atom/browser/api/atom_api_browser_window.cc +++ b/atom/browser/api/atom_api_browser_window.cc @@ -9,6 +9,7 @@ #include "atom/browser/web_contents_preferences.h" #include "atom/browser/window_list.h" #include "atom/common/api/api_messages.h" +#include "atom/common/api/constructor.h" #include "atom/common/color_util.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/value_converter.h" @@ -428,17 +429,9 @@ void Initialize(v8::Local exports, v8::Local context, void* priv) { v8::Isolate* isolate = context->GetIsolate(); - // Calling SetConstructor would only use TopLevelWindow's prototype. - v8::Local templ = CreateFunctionTemplate( - isolate, - base::Bind( - &mate::internal::InvokeNew, - base::Bind(&BrowserWindow::New))); - templ->InstanceTemplate()->SetInternalFieldCount(1); - BrowserWindow::BuildPrototype(isolate, templ); - mate::Dictionary dict(isolate, exports); - dict.Set("BrowserWindow", templ->GetFunction()); + dict.Set("BrowserWindow", mate::CreateConstructor( + isolate, base::Bind(&BrowserWindow::New))); } } // namespace diff --git a/atom/browser/api/atom_api_layout_manager.cc b/atom/browser/api/atom_api_layout_manager.cc new file mode 100644 index 000000000000..49c8cea5e748 --- /dev/null +++ b/atom/browser/api/atom_api_layout_manager.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_layout_manager.h" + +#include "atom/common/api/constructor.h" +#include "native_mate/dictionary.h" + +#include "atom/common/node_includes.h" + +namespace atom { + +namespace api { + +LayoutManager::LayoutManager(views::LayoutManager* layout_manager) + : layout_manager_(layout_manager) { + DCHECK(layout_manager_); +} + +LayoutManager::~LayoutManager() { + if (managed_by_us_) + delete layout_manager_; +} + +std::unique_ptr LayoutManager::TakeOver() { + if (!managed_by_us_) // already taken over. + return nullptr; + managed_by_us_ = false; + return std::unique_ptr(layout_manager_); +} + +// static +mate::WrappableBase* LayoutManager::New(mate::Arguments* args) { + args->ThrowError("LayoutManager can not be created directly"); + return nullptr; +} + +// static +void LayoutManager::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) {} + +} // namespace api + +} // namespace atom + +namespace { + +using atom::api::LayoutManager; + +void Initialize(v8::Local exports, + v8::Local unused, + v8::Local context, + void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.Set("LayoutManager", mate::CreateConstructor( + isolate, base::Bind(&LayoutManager::New))); +} + +} // namespace + +NODE_BUILTIN_MODULE_CONTEXT_AWARE(atom_browser_layout_manager, Initialize) diff --git a/atom/browser/api/atom_api_layout_manager.h b/atom/browser/api/atom_api_layout_manager.h new file mode 100644 index 000000000000..4e893b746b80 --- /dev/null +++ b/atom/browser/api/atom_api_layout_manager.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_LAYOUT_MANAGER_H_ +#define ATOM_BROWSER_API_ATOM_API_LAYOUT_MANAGER_H_ + +#include + +#include "atom/browser/api/trackable_object.h" +#include "ui/views/layout/layout_manager.h" + +namespace atom { + +namespace api { + +class LayoutManager : public mate::TrackableObject { + public: + static mate::WrappableBase* New(mate::Arguments* args); + + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + // Take over the ownership of the LayoutManager, and leave weak ref here. + std::unique_ptr TakeOver(); + + views::LayoutManager* layout_manager() const { return layout_manager_; } + + protected: + explicit LayoutManager(views::LayoutManager* layout_manager); + ~LayoutManager() override; + + private: + bool managed_by_us_ = true; + views::LayoutManager* layout_manager_; + + DISALLOW_COPY_AND_ASSIGN(LayoutManager); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_LAYOUT_MANAGER_H_ diff --git a/atom/browser/api/atom_api_view.cc b/atom/browser/api/atom_api_view.cc index 07db4bfca17a..9094fd5476bb 100644 --- a/atom/browser/api/atom_api_view.cc +++ b/atom/browser/api/atom_api_view.cc @@ -23,6 +23,27 @@ View::~View() { delete view_; } +#if defined(ENABLE_VIEW_API) +void View::SetLayoutManager(mate::Handle layout_manager) { + layout_manager_.Reset(isolate(), layout_manager->GetWrapper()); + // TODO(zcbenz): New versions of Chrome takes std::unique_ptr instead of raw + // pointer, remove the "release()" call when we upgraded to it. + view()->SetLayoutManager(layout_manager->TakeOver().release()); +} + +void View::AddChildView(mate::Handle child) { + AddChildViewAt(child, child_views_.size()); +} + +void View::AddChildViewAt(mate::Handle child, size_t index) { + if (index > child_views_.size()) + return; + child_views_.emplace(child_views_.begin() + index, // index + isolate(), child->GetWrapper()); // v8::Global(args...) + view()->AddChildViewAt(child->view(), index); +} +#endif + // static mate::WrappableBase* View::New(mate::Arguments* args) { auto* view = new View(); @@ -32,7 +53,15 @@ mate::WrappableBase* View::New(mate::Arguments* args) { // static void View::BuildPrototype(v8::Isolate* isolate, - v8::Local prototype) {} + v8::Local prototype) { + prototype->SetClassName(mate::StringToV8(isolate, "View")); +#if defined(ENABLE_VIEW_API) + mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) + .SetMethod("setLayoutManager", &View::SetLayoutManager) + .SetMethod("addChildView", &View::AddChildView) + .SetMethod("addChildViewAt", &View::AddChildViewAt); +#endif +} } // namespace api diff --git a/atom/browser/api/atom_api_view.h b/atom/browser/api/atom_api_view.h index 4e851ef28983..03b4e10823c9 100644 --- a/atom/browser/api/atom_api_view.h +++ b/atom/browser/api/atom_api_view.h @@ -6,8 +6,10 @@ #define ATOM_BROWSER_API_ATOM_API_VIEW_H_ #include +#include -#include "atom/browser/api/trackable_object.h" +#include "atom/browser/api/atom_api_layout_manager.h" +#include "native_mate/handle.h" #include "ui/views/view.h" namespace atom { @@ -21,6 +23,12 @@ class View : public mate::TrackableObject { static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); +#if defined(ENABLE_VIEW_API) + void SetLayoutManager(mate::Handle layout_manager); + void AddChildView(mate::Handle view); + void AddChildViewAt(mate::Handle view, size_t index); +#endif + views::View* view() const { return view_; } protected: @@ -32,6 +40,9 @@ class View : public mate::TrackableObject { void set_delete_view(bool should) { delete_view_ = should; } private: + v8::Global layout_manager_; + std::vector> child_views_; + bool delete_view_ = true; views::View* view_ = nullptr; diff --git a/atom/browser/api/atom_api_web_contents_view.cc b/atom/browser/api/atom_api_web_contents_view.cc index de27f8eeed64..60d04ff936ba 100644 --- a/atom/browser/api/atom_api_web_contents_view.cc +++ b/atom/browser/api/atom_api_web_contents_view.cc @@ -5,6 +5,7 @@ #include "atom/browser/api/atom_api_web_contents_view.h" #include "atom/browser/api/atom_api_web_contents.h" +#include "atom/common/api/constructor.h" #include "brightray/browser/inspectable_web_contents_view.h" #include "content/public/browser/web_contents_user_data.h" #include "native_mate/dictionary.h" @@ -117,13 +118,9 @@ void Initialize(v8::Local exports, v8::Local context, void* priv) { v8::Isolate* isolate = context->GetIsolate(); - WebContentsView::SetConstructor(isolate, base::Bind(&WebContentsView::New)); - - mate::Dictionary constructor( - isolate, WebContentsView::GetConstructor(isolate)->GetFunction()); - mate::Dictionary dict(isolate, exports); - dict.Set("WebContentsView", constructor); + dict.Set("WebContentsView", mate::CreateConstructor( + isolate, base::Bind(&WebContentsView::New))); } } // namespace diff --git a/atom/browser/ui/cocoa/delayed_native_view_host.cc b/atom/browser/ui/cocoa/delayed_native_view_host.cc index 0d48668895d0..95d369bfd82c 100644 --- a/atom/browser/ui/cocoa/delayed_native_view_host.cc +++ b/atom/browser/ui/cocoa/delayed_native_view_host.cc @@ -14,7 +14,7 @@ DelayedNativeViewHost::~DelayedNativeViewHost() {} void DelayedNativeViewHost::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { NativeViewHost::ViewHierarchyChanged(details); - if (details.is_add) + if (details.is_add && GetWidget()) Attach(native_view_); } diff --git a/atom/common/api/constructor.h b/atom/common/api/constructor.h new file mode 100644 index 000000000000..a4e4332f0a55 --- /dev/null +++ b/atom/common/api/constructor.h @@ -0,0 +1,33 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_COMMON_API_CONSTRUCTOR_H_ +#define ATOM_COMMON_API_CONSTRUCTOR_H_ + +#include "native_mate/constructor.h" + +namespace mate { + +// Create a FunctionTemplate that can be "new"ed in JavaScript. +// It is user's responsibility to ensure this function is called for one type +// only ONCE in the program's whole lifetime, otherwise we would have memory +// leak. +template +v8::Local CreateConstructor(v8::Isolate* isolate, + const base::Callback& func) { +#ifndef NDEBUG + static bool called = false; + CHECK(!called) << "CreateConstructor can only be called for one type once"; + called = true; +#endif + v8::Local templ = CreateFunctionTemplate( + isolate, base::Bind(&mate::internal::InvokeNew, func)); + templ->InstanceTemplate()->SetInternalFieldCount(1); + T::BuildPrototype(isolate, templ); + return templ->GetFunction(); +} + +} // namespace mate + +#endif // ATOM_COMMON_API_CONSTRUCTOR_H_ diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index d50327934c8d..e939de070865 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -65,6 +65,10 @@ V(atom_renderer_ipc) \ V(atom_renderer_web_frame) +#define ELECTRON_VIEW_MODULES(V) \ + V(atom_browser_box_layout) \ + V(atom_browser_layout_manager) + // This is used to load built-in modules. Instead of using // __attribute__((constructor)), we call the _register_ // function for each built-in modules explicitly. This is only @@ -72,6 +76,9 @@ // implementation when calling the NODE_BUILTIN_MODULE_CONTEXT_AWARE. #define V(modname) void _register_##modname(); ELECTRON_BUILTIN_MODULES(V) +#if defined(ENABLE_VIEW_API) +ELECTRON_VIEW_MODULES(V) +#endif #undef V namespace { @@ -165,6 +172,9 @@ NodeBindings::~NodeBindings() { void NodeBindings::RegisterBuiltinModules() { #define V(modname) _register_##modname(); ELECTRON_BUILTIN_MODULES(V) +#if defined(ENABLE_VIEW_API) + ELECTRON_VIEW_MODULES(V) +#endif #undef V } diff --git a/electron.gyp b/electron.gyp index fec9239e480e..b040ee1ab876 100644 --- a/electron.gyp +++ b/electron.gyp @@ -38,6 +38,11 @@ 'ENABLE_RUN_AS_NODE', ], }], # enable_run_as_node + ['enable_view_api==1', { + 'defines': [ + 'ENABLE_VIEW_API', + ], + }], # enable_view_api ], }, 'targets': [ diff --git a/features.gypi b/features.gypi index 247d828bbc01..b3dca5da2ca0 100644 --- a/features.gypi +++ b/features.gypi @@ -5,9 +5,11 @@ 'enable_osr%': 1, # FIXME(alexeykuzmin) 'enable_pdf_viewer%': 0, # FIXME(deepak1556) 'enable_run_as_node%': 1, + 'enable_view_api%': 0, }, 'enable_osr%': '<(enable_osr)', 'enable_pdf_viewer%': '<(enable_pdf_viewer)', 'enable_run_as_node%': '<(enable_run_as_node)', + 'enable_view_api%': '<(enable_view_api)', }, } diff --git a/filenames.gypi b/filenames.gypi index ec0c59f0a020..4cecf4b896a3 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -436,6 +436,7 @@ 'atom/common/api/atom_api_v8_util.cc', 'atom/common/api/atom_bindings.cc', 'atom/common/api/atom_bindings.h', + 'atom/common/api/constructor.h', 'atom/common/api/event_emitter_caller.cc', 'atom/common/api/event_emitter_caller.h', 'atom/common/api/features.cc', @@ -774,6 +775,18 @@ 'atom/app/node_main.h', ], }], # enable_run_as_node + ['enable_view_api==1', { + 'js_sources': [ + 'lib/browser/api/box-layout.js', + 'lib/browser/api/layout-manager.js', + ], + 'lib_sources': [ + 'atom/browser/api/atom_api_box_layout.cc', + 'atom/browser/api/atom_api_box_layout.h', + 'atom/browser/api/atom_api_layout_manager.cc', + 'atom/browser/api/atom_api_layout_manager.h', + ], + }], # enable_view_api ['mas_build==1', { 'lib_sources': [ 'atom/browser/api/atom_api_app_mas.mm', diff --git a/lib/browser/api/box-layout.js b/lib/browser/api/box-layout.js new file mode 100644 index 000000000000..4dc77f319407 --- /dev/null +++ b/lib/browser/api/box-layout.js @@ -0,0 +1,15 @@ +'use strict' + +const electron = require('electron') + +const {LayoutManager} = electron +const {BoxLayout} = process.atomBinding('box_layout') + +Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype) + +BoxLayout.prototype._init = function () { + // Call parent class's _init. + LayoutManager.prototype._init.call(this) +} + +module.exports = BoxLayout diff --git a/lib/browser/api/layout-manager.js b/lib/browser/api/layout-manager.js new file mode 100644 index 000000000000..1a850a53450e --- /dev/null +++ b/lib/browser/api/layout-manager.js @@ -0,0 +1,8 @@ +'use strict' + +const {LayoutManager} = process.atomBinding('layout_manager') + +LayoutManager.prototype._init = function () { +} + +module.exports = LayoutManager diff --git a/lib/browser/api/module-list.js b/lib/browser/api/module-list.js index 5b218838acf9..663da97c3e6f 100644 --- a/lib/browser/api/module-list.js +++ b/lib/browser/api/module-list.js @@ -2,6 +2,7 @@ module.exports = [ {name: 'app', file: 'app'}, {name: 'autoUpdater', file: 'auto-updater'}, + {name: 'BoxLayout', file: 'box-layout'}, {name: 'BrowserView', file: 'browser-view'}, {name: 'BrowserWindow', file: 'browser-window'}, {name: 'contentTracing', file: 'content-tracing'}, @@ -9,6 +10,7 @@ module.exports = [ {name: 'globalShortcut', file: 'global-shortcut'}, {name: 'ipcMain', file: 'ipc-main'}, {name: 'inAppPurchase', file: 'in-app-purchase'}, + {name: 'LayoutManager', file: 'layout-manager'}, {name: 'Menu', file: 'menu'}, {name: 'MenuItem', file: 'menu-item'}, {name: 'net', file: 'net'},