blob: 4a297ebea261a14d92f124d23a6c23e81083799c [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/views/native_widget_mojo.h"
#include "base/bind.h"
#include "base/thread_task_runner_handle.h"
#include "mojo/aura/surface_context_factory.h"
#include "mojo/converters/geometry/geometry_type_converters.h"
#include "mojo/converters/input_events/input_events_type_converters.h"
#include "ui/compositor/compositor.h"
#include "ui/events/event.h"
#include "ui/native_theme/native_theme_aura.h"
#include "ui/views/ime/input_method.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/native_widget_delegate.h"
#include "ui/views/widget/widget_delegate.h"
namespace gfx {
class Canvas;
}
namespace mojo {
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMojo, public:
NativeWidgetMojo::NativeWidgetMojo(
views::internal::NativeWidgetDelegate* delegate,
mojo::Shell* shell,
mojo::View* view)
: delegate_(delegate),
ownership_(views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET),
view_(view),
destroying_(false),
close_widget_factory_(this) {
context_factory_.reset(new SurfaceContextFactory(shell, view));
compositor_.reset(new ui::Compositor(gfx::kNullAcceleratedWidget,
context_factory_.get(),
base::ThreadTaskRunnerHandle::Get()));
gfx::Size size = view->bounds().To<gfx::Rect>().size();
compositor_->SetScaleAndSize(1.0f, size);
}
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMojo, internal::NativeWidgetPrivate implementation:
void NativeWidgetMojo::InitNativeWidget(
const views::Widget::InitParams& params) {
ownership_ = params.ownership;
SetLayer(new ui::Layer(ui::LAYER_TEXTURED));
layer()->SetVisible(true);
layer()->set_delegate(this);
compositor_->SetRootLayer(layer());
// TODO(erg): The aura version sets shadows types on their window here.
if (params.type == views::Widget::InitParams::TYPE_CONTROL)
view_->SetVisible(true);
delegate_->OnNativeWidgetCreated(false);
gfx::Rect window_bounds = params.bounds;
// TODO(erg): Handle params.child once we have child widgets, along with
// params.parent.
// Set properties before adding to the parent so that its layout manager sees
// the correct values.
OnSizeConstraintsChanged();
// TODO(erg): Handle parent relations here.
// Start observing property changes.
view_->AddObserver(this);
// TODO(erg): Do additional things related to the restore bounds and
// maximization. See NWA.
SetBounds(window_bounds);
}
views::NonClientFrameView* NativeWidgetMojo::CreateNonClientFrameView() {
return nullptr;
}
bool NativeWidgetMojo::ShouldUseNativeFrame() const {
// TODO(erg): We can probably remove this method; it's only used in windows
// for Aero frames.
return false;
}
bool NativeWidgetMojo::ShouldWindowContentsBeTransparent() const {
return false;
}
void NativeWidgetMojo::FrameTypeChanged() {
// This is called when the Theme has changed; forward the event to the root
// widget.
GetWidget()->ThemeChanged();
GetWidget()->GetRootView()->SchedulePaint();
}
views::Widget* NativeWidgetMojo::GetWidget() {
return delegate_->AsWidget();
}
const views::Widget* NativeWidgetMojo::GetWidget() const {
return delegate_->AsWidget();
}
gfx::NativeView NativeWidgetMojo::GetNativeView() const {
// TODO(erg): Actually implementing this will require moving off aura
// entirely because of the NativeView typedefs. (Those will need to be
// changed to mojo::View being the native view/window.)
return nullptr;
}
gfx::NativeWindow NativeWidgetMojo::GetNativeWindow() const {
return nullptr;
}
views::Widget* NativeWidgetMojo::GetTopLevelWidget() {
NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
return native_widget ? native_widget->GetWidget() : nullptr;
}
const ui::Compositor* NativeWidgetMojo::GetCompositor() const {
return compositor_.get();
}
ui::Compositor* NativeWidgetMojo::GetCompositor() {
return compositor_.get();
}
ui::Layer* NativeWidgetMojo::GetLayer() {
return layer();
}
void NativeWidgetMojo::ReorderNativeViews() {
// TODO(erg): Punting on this until we actually have something that uses
// ui::Layer or NativeViewHost.
NOTIMPLEMENTED();
}
void NativeWidgetMojo::ViewRemoved(views::View* view) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::SetNativeWindowProperty(const char* name, void* value) {
// TODO(erg): "Native Window" properties do not actually set things on the
// system's native window; in mojo::View parlance, this is actually a local
// property, not a shared one.
NOTIMPLEMENTED();
}
void* NativeWidgetMojo::GetNativeWindowProperty(const char* name) const {
NOTIMPLEMENTED();
return nullptr;
}
views::TooltipManager* NativeWidgetMojo::GetTooltipManager() const {
NOTIMPLEMENTED();
return nullptr;
}
void NativeWidgetMojo::SetCapture() {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::ReleaseCapture() {
NOTIMPLEMENTED();
}
bool NativeWidgetMojo::HasCapture() const {
NOTIMPLEMENTED();
return false;
}
views::InputMethod* NativeWidgetMojo::CreateInputMethod() {
// TODO(erg): Implement this; having a NOTIMPLEMENTED() spews too much.
return nullptr;
}
views::internal::InputMethodDelegate*
NativeWidgetMojo::GetInputMethodDelegate() {
NOTIMPLEMENTED();
return nullptr;
}
ui::InputMethod* NativeWidgetMojo::GetHostInputMethod() {
NOTIMPLEMENTED();
return nullptr;
}
void NativeWidgetMojo::CenterWindow(const gfx::Size& size) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::GetWindowPlacement(
gfx::Rect* bounds,
ui::WindowShowState* show_state) const {
// The interface specifies returning restored bounds, not current bounds.
*bounds = GetRestoredBounds();
// TODO(erg): Redo how we encode |show_state|.
}
bool NativeWidgetMojo::SetWindowTitle(const base::string16& title) {
NOTIMPLEMENTED();
return true;
}
void NativeWidgetMojo::SetWindowIcons(const gfx::ImageSkia& window_icon,
const gfx::ImageSkia& app_icon) {
// TODO(erg): If we add an interface for this, we should change the method
// type to pass a representation of different icon sizes. The current
// signature is a legacy of how Windows passes icons around.
}
void NativeWidgetMojo::InitModalType(ui::ModalType modal_type) {
NOTIMPLEMENTED();
}
gfx::Rect NativeWidgetMojo::GetWindowBoundsInScreen() const {
NOTIMPLEMENTED();
return gfx::Rect();
}
gfx::Rect NativeWidgetMojo::GetClientAreaBoundsInScreen() const {
// View-to-screen coordinate system transformations depend on this returning
// the full window bounds, for example View::ConvertPointToScreen().
NOTIMPLEMENTED();
return gfx::Rect();
}
gfx::Rect NativeWidgetMojo::GetRestoredBounds() const {
NOTIMPLEMENTED();
return gfx::Rect();
}
void NativeWidgetMojo::SetBounds(const gfx::Rect& bounds) {
if (!view_)
return;
view_->SetBounds(*mojo::Rect::From(bounds));
gfx::Size size = bounds.size();
compositor_->SetScaleAndSize(1.0f, size);
layer()->SetBounds(gfx::Rect(size));
delegate_->OnNativeWidgetSizeChanged(size);
}
void NativeWidgetMojo::SetSize(const gfx::Size& size) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::StackAbove(gfx::NativeView native_view) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::StackAtTop() {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::StackBelow(gfx::NativeView native_view) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::SetShape(gfx::NativeRegion region) {
// TODO(erg): This is incomplete here. We also need to set the shape on the
// mojo::View.
NOTIMPLEMENTED();
if (view_)
layer()->SetAlphaShape(make_scoped_ptr(region));
else
delete region;
}
void NativeWidgetMojo::Close() {
// |window_| may already be deleted by parent window. This can happen
// when this widget is child widget or has transient parent
// and ownership is WIDGET_OWNS_NATIVE_WIDGET.
DCHECK(view_ ||
ownership_ == views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
if (view_) {
Hide();
// TODO(erg): Undo window modality here.
}
if (!close_widget_factory_.HasWeakPtrs()) {
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&NativeWidgetMojo::CloseNow,
close_widget_factory_.GetWeakPtr()));
}
}
void NativeWidgetMojo::CloseNow() {
view_->Destroy();
}
void NativeWidgetMojo::Show() {
ShowWithWindowState(ui::SHOW_STATE_NORMAL);
}
void NativeWidgetMojo::Hide() {
if (view_)
view_->SetVisible(false);
}
void NativeWidgetMojo::ShowMaximizedWithBounds(
const gfx::Rect& restored_bounds) {
// TODO(erg): Save the restored bounds.
ShowWithWindowState(ui::SHOW_STATE_MAXIMIZED);
}
void NativeWidgetMojo::ShowWithWindowState(ui::WindowShowState state) {
if (!view_)
return;
// TODO(erg): Set |show_state|, if it still makes sense.
view_->SetVisible(true);
if (delegate_->CanActivate()) {
if (state != ui::SHOW_STATE_INACTIVE)
Activate();
// SetInitialFocus() should be always be called, even for
// SHOW_STATE_INACTIVE. If the window has to stay inactive, the method will
// do the right thing.
SetInitialFocus(state);
}
}
bool NativeWidgetMojo::IsVisible() const {
return view_ && view_->visible();
}
void NativeWidgetMojo::Activate() {
// TODO(erg): Need to plumb activation to the window manager here.
NOTIMPLEMENTED();
}
void NativeWidgetMojo::Deactivate() {
// TODO(erg): Need to plumb deactivation to the window manager here.
NOTIMPLEMENTED();
}
bool NativeWidgetMojo::IsActive() const {
return true;
}
void NativeWidgetMojo::SetAlwaysOnTop(bool on_top) {
// TODO(erg): Plumb this to the window manager.
NOTIMPLEMENTED();
}
bool NativeWidgetMojo::IsAlwaysOnTop() const {
return false;
}
void NativeWidgetMojo::SetVisibleOnAllWorkspaces(bool always_visible) {
// Not implemented on chromeos or for child widgets.
}
void NativeWidgetMojo::Maximize() {
// TODO(erg): Everything about minimization/maximization should really be
// part of the window manager; these methods should be replaced with
// something like "RequestMaximize()".
NOTIMPLEMENTED();
}
void NativeWidgetMojo::Minimize() {
NOTIMPLEMENTED();
}
bool NativeWidgetMojo::IsMaximized() const {
return false;
}
bool NativeWidgetMojo::IsMinimized() const {
return false;
}
void NativeWidgetMojo::Restore() {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::SetFullscreen(bool fullscreen) {
NOTIMPLEMENTED();
}
bool NativeWidgetMojo::IsFullscreen() const {
return false;
}
void NativeWidgetMojo::SetOpacity(unsigned char opacity) {
// TODO(erg): Also need this on mojo::View.
if (layer())
layer()->SetOpacity(opacity / 255.0);
}
void NativeWidgetMojo::SetUseDragFrame(bool use_drag_frame) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::FlashFrame(bool flash) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::RunShellDrag(views::View* view,
const ui::OSExchangeData& data,
const gfx::Point& location,
int operation,
ui::DragDropTypes::DragEventSource source) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::SchedulePaintInRect(const gfx::Rect& rect) {
layer()->SchedulePaint(rect);
}
void NativeWidgetMojo::SetCursor(gfx::NativeCursor cursor) {
// TODO(erg): Requires a cursor client. Is so spammy that we don't put down a
// NOTIMPLEMENTED().
}
bool NativeWidgetMojo::IsMouseEventsEnabled() const {
// TODO(erg): Requires a cursor client.
NOTIMPLEMENTED();
return true;
}
void NativeWidgetMojo::ClearNativeFocus() {
// TODO(erg): Requires an aura::client::FocusClient equivalent.
}
gfx::Rect NativeWidgetMojo::GetWorkAreaBoundsInScreen() const {
// TODO(erg): Requires a gfx::Screen equivalent.
return gfx::Rect();
}
views::Widget::MoveLoopResult NativeWidgetMojo::RunMoveLoop(
const gfx::Vector2d& drag_offset,
views::Widget::MoveLoopSource source,
views::Widget::MoveLoopEscapeBehavior escape_behavior) {
// TODO(erg): This is really complicated. Requires an
// aura::client::WindowMoveClient equivalent.
NOTIMPLEMENTED();
return views::Widget::MOVE_LOOP_CANCELED;
}
void NativeWidgetMojo::EndMoveLoop() {
// TODO(erg): Requires an aura::client::WindowMoveClient equivalent.
NOTIMPLEMENTED();
}
void NativeWidgetMojo::SetVisibilityChangedAnimationsEnabled(bool value) {
NOTIMPLEMENTED();
}
ui::NativeTheme* NativeWidgetMojo::GetNativeTheme() const {
// TODO(erg): NativeThemeAura does not actually use aura. When we remove
// aura, this should be renamed to not be confusing.
return ui::NativeThemeAura::instance();
}
void NativeWidgetMojo::OnRootViewLayout() {
}
bool NativeWidgetMojo::IsTranslucentWindowOpacitySupported() const {
return true;
}
void NativeWidgetMojo::OnSizeConstraintsChanged() {
// TODO(erg): In the aura version, we set Can{Maximize,Minimize,Resize}
// properties on our backing window from the widget_delegate(). Figure out
// what we do policy wise here.
}
void NativeWidgetMojo::RepostNativeEvent(gfx::NativeEvent native_event) {
// TODO(erg): Remove this method from the whole views system. The one caller
// is in WebDialogView, which I assume we'll never use in mojo.
NOTREACHED();
}
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMojo, ui::EventHandler implementation:
void NativeWidgetMojo::OnKeyEvent(ui::KeyEvent* event) {
// TODO(erg): Key event handling is a pile of hurt. Rebuilding all of this
// means dealing with the IME. For now, punt.
NOTIMPLEMENTED();
}
void NativeWidgetMojo::OnMouseEvent(ui::MouseEvent* event) {
DCHECK(view_);
DCHECK(view_->visible());
if (event->type() == ui::ET_MOUSEWHEEL) {
delegate_->OnMouseEvent(event);
if (event->handled())
return;
}
// TODO(erg): In the aura version, we do something with the
// ToopltipManagerAura here.
delegate_->OnMouseEvent(event);
}
void NativeWidgetMojo::OnScrollEvent(ui::ScrollEvent* event) {
delegate_->OnScrollEvent(event);
}
void NativeWidgetMojo::OnGestureEvent(ui::GestureEvent* event) {
DCHECK(view_);
DCHECK(view_->visible() || event->IsEndingEvent());
delegate_->OnGestureEvent(event);
}
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMojo, ViewObserver implementation:
void NativeWidgetMojo::OnViewDestroying(View* view) {
view->RemoveObserver(this);
delegate_->OnNativeWidgetDestroying();
}
void NativeWidgetMojo::OnViewDestroyed(View* view) {
DCHECK_EQ(view, view_);
view_ = nullptr;
delegate_->OnNativeWidgetDestroyed();
if (ownership_ == views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
delete this;
}
void NativeWidgetMojo::OnViewBoundsChanged(View* view,
const Rect& mojo_old_bounds,
const Rect& mojo_new_bounds) {
gfx::Rect old_bounds = mojo_old_bounds.To<gfx::Rect>();
gfx::Rect new_bounds = mojo_new_bounds.To<gfx::Rect>();
gfx::Size size = new_bounds.size();
compositor_->SetScaleAndSize(1.0f, size);
layer()->SetBounds(gfx::Rect(size));
// Assume that if the old bounds was completely empty a move happened. This
// handles the case of a maximize animation acquiring the layer (acquiring a
// layer results in clearing the bounds).
if (old_bounds.origin() != new_bounds.origin() ||
(old_bounds == gfx::Rect(0, 0, 0, 0) && !new_bounds.IsEmpty())) {
delegate_->OnNativeWidgetMove();
}
if (old_bounds.size() != new_bounds.size())
delegate_->OnNativeWidgetSizeChanged(new_bounds.size());
}
void NativeWidgetMojo::OnViewFocusChanged(View* gained_focus,
View* lost_focus) {
if (view_ == gained_focus) {
if (GetWidget()->GetInputMethod()) // Null in tests.
GetWidget()->GetInputMethod()->OnFocus();
// TODO(erg): We need to set mojo::View* as the gfx::NativeView. Once that
// is done, we can uncomment the line below.
//
// delegate_->OnNativeFocus(lost_focus);
} else if (view_ == lost_focus) {
// GetInputMethod() recreates the input method if it's previously been
// destroyed. If we get called during destruction, the input method will be
// gone, and creating a new one and telling it that we lost the focus will
// trigger a DCHECK (the new input method doesn't think that we have the
// focus and doesn't expect a blur). OnBlur() shouldn't be called during
// destruction unless WIDGET_OWNS_NATIVE_WIDGET is set (which is just the
// case in tests).
if (!destroying_) {
if (GetWidget()->GetInputMethod())
GetWidget()->GetInputMethod()->OnBlur();
} else {
DCHECK_EQ(ownership_,
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
}
// TODO(erg): We need to set mojo::View* as the gfx::NativeView. Once that
// is done, we can uncomment the line below.
//
// delegate_->OnNativeBlur(gained_focus);
}
}
void NativeWidgetMojo::OnViewInputEvent(View* view, const EventPtr& event) {
scoped_ptr<ui::Event> ui_event(event.To<scoped_ptr<ui::Event>>());
if (ui_event)
OnEvent(ui_event.get());
}
// TODO(erg): Add an OnViewActivationChanged() method and plumb that from the
// window manager through the view manager to here.
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMojo, LayerDelegate:
void NativeWidgetMojo::OnPaintLayer(gfx::Canvas* canvas) {
delegate_->OnNativeWidgetPaint(canvas);
}
void NativeWidgetMojo::OnDelegatedFrameDamage(
const gfx::Rect& damage_rect_in_dip) {
NOTIMPLEMENTED();
}
void NativeWidgetMojo::OnDeviceScaleFactorChanged(float device_scale_factor) {
NOTIMPLEMENTED();
}
base::Closure NativeWidgetMojo::PrepareForLayerBoundsChange() {
// TODO(erg): This needs a real implementation, but we don't NOTIMPLEMENTED()
// because spam.
return base::Closure();
}
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMojo, protected:
NativeWidgetMojo::~NativeWidgetMojo() {
destroying_ = true;
if (ownership_ == views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
delete delegate_;
else
CloseNow();
}
////////////////////////////////////////////////////////////////////////////////
// NativeWidgetMojo, private:
void NativeWidgetMojo::SetInitialFocus(ui::WindowShowState show_state) {
NOTIMPLEMENTED();
}
namespace internal {
////////////////////////////////////////////////////////////////////////////////
// internal::NativeWidgetPrivate, public:
// TODO(erg): Re-implement the static NativeWidgetPrivate interface here. This
// is already implemented in NativeWidgetAura, and we can't reimplement it here
// until we delete it there.
} // namespace internal
} // namespace mojo