Index: ui/views/cocoa/bridged_native_widget.mm |
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm |
index 560f533009df6cd8efb4132130a2ed2640e7074a..9ad0ffeda8afae70ba847ce2b95d392568aa8191 100644 |
--- a/ui/views/cocoa/bridged_native_widget.mm |
+++ b/ui/views/cocoa/bridged_native_widget.mm |
@@ -10,7 +10,9 @@ |
#include "ui/base/ime/input_method.h" |
#include "ui/base/ime/input_method_factory.h" |
#include "ui/base/ui_base_switches_util.h" |
+#include "ui/gfx/display.h" |
#import "ui/gfx/mac/coordinate_conversion.h" |
+#include "ui/gfx/screen.h" |
#import "ui/views/cocoa/bridged_content_view.h" |
#import "ui/views/cocoa/views_nswindow_delegate.h" |
#include "ui/views/widget/native_widget_mac.h" |
@@ -19,13 +21,35 @@ |
#include "ui/views/view.h" |
#include "ui/views/widget/widget.h" |
+#include "base/bind.h" |
+ |
+#include "cc/output/copy_output_request.h" |
+#include "cc/output/copy_output_result.h" |
+ |
+#include "third_party/skia/include/core/SkBitmap.h" |
+#include "ui/gfx/codec/png_codec.h" |
+#include "ui/gfx/image/image_skia_rep.h" |
+#include "ui/gfx/canvas.h" |
+ |
+namespace { |
+ |
+float GetDeviceScaleFactorFromDisplay(NSView* view) { |
+ gfx::Display display = gfx::Screen::GetScreenFor(view)-> |
+ GetDisplayNearestWindow(view); |
+ DCHECK(display.is_valid()); |
+ return display.device_scale_factor(); |
+} |
+ |
+} // namespace |
+ |
namespace views { |
BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) |
: native_widget_mac_(parent), |
focus_manager_(NULL), |
target_fullscreen_state_(false), |
- in_fullscreen_transition_(false) { |
+ in_fullscreen_transition_(false), |
+ window_visible_(false) { |
DCHECK(parent); |
window_delegate_.reset( |
[[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); |
@@ -50,6 +74,18 @@ void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window, |
window_.swap(window); |
[window_ setDelegate:window_delegate_]; |
+ // Register for application hide notifications so that visibility can be |
+ // properly tracked. This is not done in the delegate so that the lifetime is |
+ // tied to the C++ object, rather than the delegate (which may be reference |
+ // counted). This is required since the application hides do not send an |
+ // orderOut: to individual windows. Unhide, however, does send an order |
+ // message. |
+ [[NSNotificationCenter defaultCenter] |
+ addObserver:window_delegate_ |
+ selector:@selector(onWindowOrderChanged:) |
+ name:NSApplicationDidHideNotification |
+ object:nil]; |
+ |
// Validate the window's initial state, otherwise the bridge's initial |
// tracking state will be incorrect. |
DCHECK(![window_ isVisible]); |
@@ -59,6 +95,13 @@ void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window, |
// Use NSWindow to manage child windows. This won't automatically close them |
// but it will maintain relative positioning of the window layer and origin. |
[[params.parent window] addChildWindow:window_ ordered:NSWindowAbove]; |
+ |
+ // Windows with a parent that are not explicitly child windows are transient |
+ // children. |
+ if (!params.child) { |
+ [window setExcludedFromWindowsMenu:YES]; |
+ [window_delegate_ setTransientChild:YES]; |
+ } |
} |
} |
@@ -75,10 +118,24 @@ void BridgedNativeWidget::SetFocusManager(FocusManager* focus_manager) { |
focus_manager_ = focus_manager; |
} |
+void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) { |
+ [window_ setFrame:gfx::ScreenRectToNSRect(new_bounds) |
+ display:YES |
+ animate:NO]; |
+ |
+ // If not visible, -[NSWindowDelegate windowDidResize:] is not triggered. |
+ if (![window_ isVisible]) |
+ OnSizeChanged(); |
+} |
+ |
void BridgedNativeWidget::SetRootView(views::View* view) { |
if (view == [bridged_view_ hostedView]) |
return; |
+ // If this is ever false, the compositor will need to be properly torn down |
+ // and replaced, pointing at the new view. |
+ DCHECK(!view || !compositor_view_); |
+ |
[bridged_view_ clearView]; |
bridged_view_.reset(); |
// Note that there can still be references to the old |bridged_view_| |
@@ -97,6 +154,7 @@ void BridgedNativeWidget::SetRootView(views::View* view) { |
void BridgedNativeWidget::OnWindowWillClose() { |
[[window_ parentWindow] removeChildWindow:window_]; |
[window_ setDelegate:nil]; |
+ [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; |
native_widget_mac_->OnWindowWillClose(); |
} |
@@ -155,6 +213,28 @@ void BridgedNativeWidget::ToggleDesiredFullscreenState() { |
[window_ setCollectionBehavior:behavior]; |
} |
+void BridgedNativeWidget::OnSizeChanged() { |
+ NSSize new_size = [window_ frame].size; |
+ native_widget_mac_->GetWidget()->OnNativeWidgetSizeChanged( |
+ gfx::Size(new_size.width, new_size.height)); |
+ if (layer()) |
+ SetLayerSize(native_widget_mac_->GetClientAreaBoundsInScreen().size()); |
+} |
+ |
+void BridgedNativeWidget::OnVisibilityChanged() { |
+ OnSizeChanged(); |
+ if (window_visible_ == [window_ isVisible]) |
+ return; |
+ |
+ window_visible_ = [window_ isVisible]; |
+ native_widget_mac_->GetWidget()->OnNativeWidgetVisibilityChanged( |
+ window_visible_); |
+} |
+ |
+void BridgedNativeWidget::OnDisplayChanged() { |
+ NOTIMPLEMENTED(); |
+} |
+ |
InputMethod* BridgedNativeWidget::CreateInputMethod() { |
if (switches::IsTextInputFocusManagerEnabled()) |
return new NullInputMethod(); |
@@ -178,6 +258,35 @@ gfx::Rect BridgedNativeWidget::GetRestoredBounds() const { |
return gfx::ScreenRectFromNSRect([window_ frame]); |
} |
+ui::Layer* BridgedNativeWidget::GetOrCreateLayer() { |
+ if (!bridged_view_) |
+ return NULL; |
+ |
+ if (layer()) |
+ return layer(); |
+ |
+ NSView* host_view = BrowserCompositorSuperview(); |
+ [host_view setWantsLayer:YES]; |
+ // Next line does nothing. |
+ [[host_view layer] setBackgroundColor:CGColorGetConstantColor(kCGColorClear)]; |
+ |
+ SetLayer(new ui::Layer()); |
+ layer()->set_delegate(this); |
+ // DCHECK_EQ(0u, [[bridged_view_ subviews] count]); |
+ compositor_view_.reset(new content::BrowserCompositorViewMac(this)); |
+ |
+ DCHECK(compositor_view_->GetCompositor()); |
+ DCHECK_EQ(compositor_view_->GetCompositor(), layer()->GetCompositor()); |
+ |
+ [window_ setOpaque:NO]; |
+ layer()->GetCompositor()->SetHostHasTransparentBackground(true); |
+ layer()->SetFillsBoundsCompletely(true); |
+ |
+ SetLayerSize(native_widget_mac_->GetClientAreaBoundsInScreen().size()); |
+ |
+ return layer(); |
+} |
+ |
//////////////////////////////////////////////////////////////////////////////// |
// BridgedNativeWidget, internal::InputMethodDelegate: |
@@ -190,6 +299,38 @@ void BridgedNativeWidget::DispatchKeyEventPostIME(const ui::KeyEvent& key) { |
focus_manager_->OnKeyEvent(key); |
} |
+//////////////////////////////////////////////////////////////////////////////// |
+// BridgedNativeWidget, content::BrowserCompositorViewMacClient: |
+ |
+bool BridgedNativeWidget::BrowserCompositorViewShouldAckImmediately() const { |
+ return false; |
+} |
+ |
+void BridgedNativeWidget::BrowserCompositorViewFrameSwapped( |
+ const std::vector<ui::LatencyInfo>& latency_info) { |
+} |
+ |
+NSView* BridgedNativeWidget::BrowserCompositorSuperview() { |
+ if (!compositor_superview_) { |
+ compositor_superview_.reset([[NSView alloc] initWithFrame:NSZeroRect]); |
+ if ([[bridged_view_ subviews] count] == 0) { |
+ [bridged_view_ addSubview:compositor_superview_]; |
+ } else { |
+ [bridged_view_ addSubview:compositor_superview_ |
+ positioned:NSWindowBelow |
+ relativeTo:[[bridged_view_ subviews] objectAtIndex:0]]; |
+ } |
+ } |
+ return compositor_superview_; |
+} |
+ |
+ui::Layer* BridgedNativeWidget::BrowserCompositorRootLayer() { |
+ return layer(); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// BridgedNativeWidget, FocusChangeListener: |
+ |
void BridgedNativeWidget::OnWillChangeFocus(View* focused_before, |
View* focused_now) { |
} |
@@ -202,6 +343,61 @@ void BridgedNativeWidget::OnDidChangeFocus(View* focused_before, |
} |
//////////////////////////////////////////////////////////////////////////////// |
+// BridgedNativeWidget, LayerDelegate: |
+ |
+static bool WritePNGFile(const SkBitmap& bitmap, const base::FilePath& file_path, |
+ bool discard_transparency) { |
+ std::vector<unsigned char> png_data; |
+ if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, |
+ discard_transparency, |
+ &png_data) && |
+ base::CreateDirectory(file_path.DirName())) { |
+ char* data = reinterpret_cast<char*>(&png_data[0]); |
+ int size = static_cast<int>(png_data.size()); |
+ bool result = base::WriteFile(file_path, data, size) == size; |
+ return result; |
+ } |
+ return false; |
+} |
+ |
+static void ReadbackResult(scoped_ptr<cc::CopyOutputResult> result) { |
+ return; |
+ DLOG(INFO) << "result!"; |
+ scoped_ptr<SkBitmap> result_bitmap = result->TakeBitmap().Pass(); |
+ static int filectr = 0; |
+ std::ostringstream oss; |
+ oss << "file" << filectr++ << ".png"; |
+ WritePNGFile(*result_bitmap, base::FilePath().AppendASCII(oss.str().c_str()), false); |
+} |
+ |
+void BridgedNativeWidget::OnPaintLayer(gfx::Canvas* canvas) { |
+ native_widget_mac_->GetWidget()->OnNativeWidgetPaint(canvas); |
+return; |
+ layer()->RequestCopyOfOutput(cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(&ReadbackResult))); |
+ native_widget_mac_->GetWidget()->OnNativeWidgetPaint(canvas); |
+ gfx::ImageSkiaRep image = canvas->ExtractImageRep(); |
+ static int filectr = 0; |
+ std::ostringstream oss; |
+ oss << "bpass" << filectr++ << ".png"; |
+ WritePNGFile(image.sk_bitmap(), base::FilePath().AppendASCII(oss.str().c_str()), false); |
+ |
+} |
+ |
+void BridgedNativeWidget::OnDelegatedFrameDamage( |
+ const gfx::Rect& damage_rect_in_dip) { |
+ NOTIMPLEMENTED(); |
+} |
+ |
+void BridgedNativeWidget::OnDeviceScaleFactorChanged( |
+ float device_scale_factor) { |
+} |
+ |
+base::Closure BridgedNativeWidget::PrepareForLayerBoundsChange() { |
+ return base::Bind(&BridgedNativeWidget::OnWindowBoundsChanged, |
+ base::Unretained(this)); |
+} |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
// BridgedNativeWidget, private: |
void BridgedNativeWidget::RemoveOrDestroyChildren() { |
@@ -211,4 +407,25 @@ void BridgedNativeWidget::RemoveOrDestroyChildren() { |
[child_windows makeObjectsPerformSelector:@selector(close)]; |
} |
+void BridgedNativeWidget::SetLayerSize(const gfx::Size& size_in_dip) { |
+ DLOG(INFO) << "SetLayerSize: " << size_in_dip.ToString(); |
+ DCHECK(layer()); |
+ DCHECK(compositor_superview_); |
+ |
+ layer()->SetBounds(gfx::Rect(size_in_dip)); |
+ NSRect view_frame = [compositor_superview_ frame]; |
+ view_frame.size.width = size_in_dip.width(); |
+ view_frame.size.height = size_in_dip.height(); |
+ [compositor_superview_ setFrame:view_frame]; |
+ |
+ float scale_factor = GetDeviceScaleFactorFromDisplay(compositor_superview_); |
+ gfx::Size size_in_pixels(size_in_dip.width() * scale_factor, |
+ size_in_dip.height() * scale_factor); |
+ compositor_view_->GetCompositor()->SetScaleAndSize(scale_factor, |
+ size_in_pixels); |
+} |
+ |
+void BridgedNativeWidget::OnWindowBoundsChanged() { |
+} |
+ |
} // namespace views |