// Copyright 2014 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 <map>
#include <string>
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "mojo/application/application_runner_chromium.h"
#include "mojo/examples/media_viewer/media_viewer.mojom.h"
#include "mojo/public/c/system/main.h"
#include "mojo/public/cpp/application/application_connection.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/public/cpp/application/interface_factory_impl.h"
#include "mojo/public/cpp/bindings/interface_impl.h"
#include "mojo/services/public/cpp/view_manager/view.h"
#include "mojo/services/public/cpp/view_manager/view_manager.h"
#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
#include "mojo/services/public/cpp/view_manager/view_observer.h"
#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
#include "mojo/views/native_widget_view_manager.h"
#include "mojo/views/views_init.h"
#include "skia/ext/platform_canvas.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkRect.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/painter.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace mojo {
namespace examples {
class MediaViewer;
class CustomButtonBorder: public views::Border {
public:
CustomButtonBorder()
: normal_painter_(CreatePainter(SkColorSetRGB(0x80, 0x80, 0x80),
SkColorSetRGB(0xC0, 0xC0, 0xC0))),
hot_painter_(CreatePainter(SkColorSetRGB(0xA0, 0xA0, 0xA0),
SkColorSetRGB(0xD0, 0xD0, 0xD0))),
pushed_painter_(CreatePainter(SkColorSetRGB(0x80, 0x80, 0x80),
SkColorSetRGB(0x90, 0x90, 0x90))),
insets_(2, 6, 2, 6) {
}
virtual ~CustomButtonBorder() {}
private:
// Overridden from views::Border:
virtual void Paint(const views::View& view, gfx::Canvas* canvas) OVERRIDE {
const views::LabelButton* button =
static_cast<const views::LabelButton*>(&view);
views::Button::ButtonState state = button->state();
views::Painter* painter = normal_painter_.get();
if (state == views::Button::STATE_HOVERED) {
painter = hot_painter_.get();
} else if (state == views::Button::STATE_PRESSED) {
painter = pushed_painter_.get();
}
painter->Paint(canvas, view.size());
}
virtual gfx::Insets GetInsets() const OVERRIDE {
return insets_;
}
virtual gfx::Size GetMinimumSize() const OVERRIDE {
gfx::Size size;
if (normal_painter_)
size.SetToMax(normal_painter_->GetMinimumSize());
if (hot_painter_)
size.SetToMax(hot_painter_->GetMinimumSize());
if (pushed_painter_)
size.SetToMax(pushed_painter_->GetMinimumSize());
return size;
}
scoped_ptr<views::Painter> CreatePainter(SkColor border, SkColor background) {
skia::RefPtr<SkCanvas> canvas(skia::AdoptRef(skia::CreatePlatformCanvas(
64, 64, false)));
SkPaint paint;
paint.setColor(background);
canvas->drawRoundRect(SkRect::MakeWH(63, 63), 2, 2, paint);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(border);
canvas->drawRoundRect(SkRect::MakeWH(63, 63), 2, 2, paint);
return scoped_ptr<views::Painter>(
views::Painter::CreateImagePainter(
gfx::ImageSkia::CreateFrom1xBitmap(
skia::GetTopDevice(*canvas)->accessBitmap(true)),
gfx::Insets(5, 5, 5, 5)));
}
scoped_ptr<views::Painter> normal_painter_;
scoped_ptr<views::Painter> hot_painter_;
scoped_ptr<views::Painter> pushed_painter_;
gfx::Insets insets_;
DISALLOW_COPY_AND_ASSIGN(CustomButtonBorder);
};
class ControlPanel : public views::ButtonListener {
public:
enum ControlType {
CONTROL_ZOOM_IN,
CONTROL_ACTUAL_SIZE,
CONTROL_ZOOM_OUT,
CONTROL_COUNT,
};
class Delegate {
public:
virtual ~Delegate() {}
virtual void ButtonPressed(ControlType type) = 0;
};
ControlPanel(Delegate* delegate) : delegate_(delegate), buttons_() {}
virtual ~ControlPanel() {}
void Initialize(View* view) {
const char* kNames[] = { "Zoom In", "Actual Size", "Zoom Out" };
views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView;
widget_delegate->GetContentsView()->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kHorizontal, 5, 2, 5));
widget_delegate->GetContentsView()->set_background(
views::Background::CreateSolidBackground(SK_ColorLTGRAY));
for (int type = 0; type < CONTROL_COUNT; ++type) {
views::Button* button = new views::LabelButton(
this, base::ASCIIToUTF16(kNames[type]));
button->SetBorder(scoped_ptr<views::Border>(new CustomButtonBorder));
buttons_[type] = button;
widget_delegate->GetContentsView()->AddChildView(button);
}
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.native_widget = new NativeWidgetViewManager(widget, view);
params.delegate = widget_delegate;
params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height());
params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
widget->Init(params);
widget->Show();
}
private:
// Overridden from views::ButtonListener:
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE {
for (int i = 0; i < CONTROL_COUNT; ++i) {
if (sender == buttons_[i]) {
delegate_->ButtonPressed(static_cast<ControlType>(i));
return;
}
}
}
Delegate* delegate_;
views::Button* buttons_[CONTROL_COUNT];
DISALLOW_COPY_AND_ASSIGN(ControlPanel);
};
class MediaViewer
: public ApplicationDelegate,
public ViewManagerDelegate,
public ControlPanel::Delegate,
public ViewObserver {
public:
MediaViewer()
: app_(NULL),
view_manager_(NULL),
root_view_(NULL),
control_view_(NULL),
content_view_(NULL),
control_panel_(this) {
handler_map_["image/png"] = "mojo:mojo_png_viewer";
}
virtual ~MediaViewer() {
if (root_view_)
root_view_->RemoveObserver(this);
}
private:
typedef std::map<std::string, std::string> HandlerMap;
// Overridden from ApplicationDelegate:
virtual void Initialize(ApplicationImpl* app) OVERRIDE {
view_manager_client_factory_.reset(
new ViewManagerClientFactory(app->shell(), this));
app_ = app;
views_init_.reset(new ViewsInit);
}
virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
OVERRIDE {
connection->AddService(view_manager_client_factory_.get());
return true;
}
void LayoutViews() {
View* root = content_view_->parent();
gfx::Rect control_bounds(root->bounds().width(), 28);
control_view_->SetBounds(control_bounds);
gfx::Rect content_bounds(0, control_bounds.height(), root->bounds().width(),
root->bounds().height() - control_bounds.height());
content_view_->SetBounds(content_bounds);
}
// Overridden from ViewManagerDelegate:
virtual void OnEmbed(ViewManager* view_manager,
View* root,
ServiceProviderImpl* exported_services,
scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
root_view_ = root;
view_manager_ = view_manager;
control_view_ = View::Create(view_manager_);
root_view_->AddChild(control_view_);
content_view_ = View::Create(view_manager_);
root_view_->AddChild(content_view_);
control_panel_.Initialize(control_view_);
LayoutViews();
root_view_->AddObserver(this);
content_view_->Embed("TODO");
}
virtual void OnViewManagerDisconnected(
ViewManager* view_manager) OVERRIDE {
DCHECK_EQ(view_manager_, view_manager);
view_manager_ = NULL;
base::MessageLoop::current()->Quit();
}
// Overridden from ControlPanel::Delegate:
virtual void ButtonPressed(ControlPanel::ControlType type) OVERRIDE {
switch (type) {
case ControlPanel::CONTROL_ZOOM_IN:
zoomable_media_->ZoomIn();
break;
case ControlPanel::CONTROL_ACTUAL_SIZE:
zoomable_media_->ZoomToActualSize();
break;
case ControlPanel::CONTROL_ZOOM_OUT:
zoomable_media_->ZoomOut();
break;
default:
NOTIMPLEMENTED();
}
}
// ViewObserver:
virtual void OnViewBoundsChanged(View* view,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE {
LayoutViews();
}
virtual void OnViewDestroyed(View* view) OVERRIDE {
DCHECK_EQ(view, root_view_);
view->RemoveObserver(this);
root_view_ = NULL;
}
std::string GetHandlerForContentType(const std::string& content_type) {
HandlerMap::const_iterator it = handler_map_.find(content_type);
return it != handler_map_.end() ? it->second : std::string();
}
scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
ApplicationImpl* app_;
scoped_ptr<ViewsInit> views_init_;
ViewManager* view_manager_;
View* root_view_;
View* control_view_;
View* content_view_;
ControlPanel control_panel_;
ZoomableMediaPtr zoomable_media_;
HandlerMap handler_map_;
DISALLOW_COPY_AND_ASSIGN(MediaViewer);
};
} // namespace examples
} // namespace mojo
MojoResult MojoMain(MojoHandle shell_handle) {
mojo::ApplicationRunnerChromium runner(new mojo::examples::MediaViewer);
return runner.Run(shell_handle);
}