blob: efe73aa6ee60588c1562c6002cae3d85baf32b94 [file] [log] [blame]
// Copyright 2015 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 "components/exo/shell_surface.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/window_state.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "components/exo/surface.h"
#include "ui/aura/window.h"
#include "ui/aura/window_property.h"
#include "ui/base/hit_test.h"
#include "ui/views/widget/widget.h"
DECLARE_WINDOW_PROPERTY_TYPE(std::string*)
namespace exo {
namespace {
class CustomFrameView : public views::NonClientFrameView {
public:
explicit CustomFrameView(views::Widget* widget) : widget_(widget) {}
~CustomFrameView() override {}
// Overridden from views::NonClientFrameView:
gfx::Rect GetBoundsForClientView() const override { return bounds(); }
gfx::Rect GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const override {
return client_bounds;
}
int NonClientHitTest(const gfx::Point& point) override {
return widget_->client_view()->NonClientHitTest(point);
}
void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override {}
void ResetWindowControls() override {}
void UpdateWindowIcon() override {}
void UpdateWindowTitle() override {}
void SizeConstraintsChanged() override {}
private:
views::Widget* const widget_;
DISALLOW_COPY_AND_ASSIGN(CustomFrameView);
};
class ShellSurfaceWidget : public views::Widget {
public:
explicit ShellSurfaceWidget(ShellSurface* shell_surface)
: shell_surface_(shell_surface) {}
// Overridden from views::Widget
void Close() override { shell_surface_->Close(); }
private:
ShellSurface* const shell_surface_;
DISALLOW_COPY_AND_ASSIGN(ShellSurfaceWidget);
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
// ShellSurface, public:
DEFINE_LOCAL_WINDOW_PROPERTY_KEY(std::string*, kApplicationIdKey, nullptr)
ShellSurface::ShellSurface(Surface* surface) : surface_(surface) {
surface_->SetSurfaceDelegate(this);
surface_->AddSurfaceObserver(this);
surface_->Show();
set_owned_by_client();
}
ShellSurface::~ShellSurface() {
if (surface_) {
surface_->SetSurfaceDelegate(nullptr);
surface_->RemoveSurfaceObserver(this);
}
if (widget_)
widget_->CloseNow();
}
void ShellSurface::Init() {
TRACE_EVENT0("exo", "ShellSurface::Init");
if (widget_) {
DLOG(WARNING) << "Shell surface already initialized";
return;
}
views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_WINDOW;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.delegate = this;
params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE;
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
params.show_state = ui::SHOW_STATE_NORMAL;
params.parent = ash::Shell::GetContainer(
ash::Shell::GetPrimaryRootWindow(), ash::kShellWindowId_DefaultContainer);
widget_.reset(new ShellSurfaceWidget(this));
widget_->Init(params);
widget_->GetNativeWindow()->set_owned_by_parent(false);
widget_->GetNativeWindow()->SetName("ExoShellSurface");
widget_->GetNativeWindow()->AddChild(surface_);
SetApplicationId(widget_->GetNativeWindow(), &application_id_);
// The position of a top-level shell surface is managed by Ash.
ash::wm::GetWindowState(widget_->GetNativeWindow())
->set_window_position_managed(true);
}
void ShellSurface::Maximize() {
TRACE_EVENT0("exo", "ShellSurface::Maximize");
DCHECK(widget_);
widget_->Maximize();
if (!configure_callback_.is_null())
configure_callback_.Run(widget_->GetWindowBoundsInScreen().size());
}
void ShellSurface::SetFullscreen(bool fullscreen) {
TRACE_EVENT1("exo", "ShellSurface::SetFullscreen", "fullscreen", fullscreen);
DCHECK(widget_);
widget_->SetFullscreen(fullscreen);
if (!configure_callback_.is_null())
configure_callback_.Run(widget_->GetWindowBoundsInScreen().size());
}
void ShellSurface::SetTitle(const base::string16& title) {
TRACE_EVENT1("exo", "ShellSurface::SetTitle", "title",
base::UTF16ToUTF8(title));
title_ = title;
if (widget_)
widget_->UpdateWindowTitle();
}
// static
void ShellSurface::SetApplicationId(aura::Window* window,
std::string* application_id) {
window->SetProperty(kApplicationIdKey, application_id);
}
// static
const std::string ShellSurface::GetApplicationId(aura::Window* window) {
std::string* string_ptr = window->GetProperty(kApplicationIdKey);
return string_ptr ? *string_ptr : std::string();
}
void ShellSurface::SetApplicationId(const std::string& application_id) {
TRACE_EVENT1("exo", "ShellSurface::SetApplicationId", "application_id",
application_id);
application_id_ = application_id;
}
void ShellSurface::Move() {
TRACE_EVENT0("exo", "ShellSurface::Move");
if (widget_) {
widget_->RunMoveLoop(gfx::Vector2d(), views::Widget::MOVE_LOOP_SOURCE_MOUSE,
views::Widget::MOVE_LOOP_ESCAPE_BEHAVIOR_DONT_HIDE);
}
}
void ShellSurface::Close() {
if (!close_callback_.is_null())
close_callback_.Run();
}
void ShellSurface::SetGeometry(const gfx::Rect& geometry) {
TRACE_EVENT1("exo", "ShellSurface::SetGeometry", "geometry",
geometry.ToString());
if (geometry.IsEmpty()) {
DLOG(WARNING) << "Surface geometry must be non-empty";
return;
}
geometry_ = geometry;
}
scoped_refptr<base::trace_event::TracedValue> ShellSurface::AsTracedValue()
const {
scoped_refptr<base::trace_event::TracedValue> value =
new base::trace_event::TracedValue;
value->SetString("title", base::UTF16ToUTF8(title_));
value->SetString("application_id", application_id_);
return value;
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceDelegate overrides:
void ShellSurface::OnSurfaceCommit() {
surface_->CommitSurfaceHierarchy();
if (widget_) {
// Update surface bounds and widget size.
gfx::Point origin;
views::View::ConvertPointToWidget(this, &origin);
surface_->SetBounds(gfx::Rect(origin - geometry_.OffsetFromOrigin(),
surface_->layer()->size()));
widget_->SetSize(widget_->non_client_view()->GetPreferredSize());
// Show widget if not already visible.
if (!widget_->IsClosed() && !widget_->IsVisible())
widget_->Show();
}
}
bool ShellSurface::IsSurfaceSynchronized() const {
// A shell surface is always desynchronized.
return false;
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceObserver overrides:
void ShellSurface::OnSurfaceDestroying(Surface* surface) {
surface->RemoveSurfaceObserver(this);
surface_ = nullptr;
// Note: In its use in the Wayland server implementation, the surface
// destroyed callback may destroy the ShellSurface instance. This call needs
// to be last so that the instance can be destroyed.
if (!surface_destroyed_callback_.is_null())
surface_destroyed_callback_.Run();
}
////////////////////////////////////////////////////////////////////////////////
// views::WidgetDelegate overrides:
base::string16 ShellSurface::GetWindowTitle() const {
return title_;
}
views::Widget* ShellSurface::GetWidget() {
return widget_.get();
}
const views::Widget* ShellSurface::GetWidget() const {
return widget_.get();
}
views::View* ShellSurface::GetContentsView() {
return this;
}
views::NonClientFrameView* ShellSurface::CreateNonClientFrameView(
views::Widget* widget) {
return new CustomFrameView(widget);
}
////////////////////////////////////////////////////////////////////////////////
// views::Views overrides:
gfx::Size ShellSurface::GetPreferredSize() const {
if (!geometry_.IsEmpty())
return geometry_.size();
return surface_ ? surface_->GetPreferredSize() : gfx::Size();
}
} // namespace exo