blob: 921e702b106efa345ddb906351c5e3c20b783a84 [file]
// 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 "services/keyboard_native/view_observer_delegate.h"
#include "base/bind.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/skia/ganesh_surface.h"
#include "services/keyboard_native/keyboard_service_impl.h"
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkTypeface.h"
namespace keyboard {
mojo::Size ToSize(const mojo::Rect& rect) {
mojo::Size size;
size.width = rect.width;
size.height = rect.height;
return size;
}
ViewObserverDelegate::ViewObserverDelegate()
: keyboard_service_impl_(nullptr),
view_(nullptr),
last_action_(-1),
key_layout_(),
last_key_(nullptr),
weak_factory_(this) {
key_layout_.SetTextCallback(
base::Bind(&ViewObserverDelegate::OnText, weak_factory_.GetWeakPtr()));
}
ViewObserverDelegate::~ViewObserverDelegate() {
}
void ViewObserverDelegate::SetKeyboardServiceImpl(
KeyboardServiceImpl* keyboard_service_impl) {
keyboard_service_impl_ = keyboard_service_impl;
}
void ViewObserverDelegate::OnViewCreated(mojo::View* view, mojo::Shell* shell) {
if (view_ != nullptr) {
view_->RemoveObserver(this);
}
view_ = view;
view_->AddObserver(this);
gl_context_ = mojo::GLContext::Create(shell);
gr_context_ = std::unique_ptr<mojo::GaneshContext>(
new mojo::GaneshContext(gl_context_));
texture_uploader_ = std::unique_ptr<mojo::TextureUploader>(
new mojo::TextureUploader(this, shell, gl_context_));
DrawKeys(ToSize(view_->bounds()));
}
void ViewObserverDelegate::OnText(const std::string& text) {
keyboard_service_impl_->OnKey(text.c_str());
}
void ViewObserverDelegate::DrawKeysToCanvas(const mojo::Size& size,
SkCanvas* canvas) {
key_layout_.SetSize(size);
key_layout_.Draw(canvas);
}
void ViewObserverDelegate::DrawKeys(const mojo::Size& size) {
mojo::GaneshContext::Scope scope(gr_context_.get());
mojo::GaneshSurface surface(
gr_context_.get(),
make_scoped_ptr(new mojo::GLTexture(gl_context_, size)));
// Need this to reset the texture binding state after setting new texture size
gr_context_.get()->gr()->resetContext(kTextureBinding_GrGLBackendState);
SkCanvas* canvas = surface.canvas();
DrawKeysToCanvas(size, canvas);
canvas->flush();
texture_uploader_->Upload(surface.TakeTexture());
}
void ViewObserverDelegate::UpdateMovePoints(mojo::Size size,
const mojo::EventPtr& event) {
static const size_t kMaxSize = 30;
switch (event->action) {
case mojo::EVENT_TYPE_POINTER_UP:
// commit point
move_points_.clear();
break;
case mojo::EVENT_TYPE_POINTER_DOWN:
case mojo::EVENT_TYPE_POINTER_MOVE:
if (event->pointer_data) {
move_points_.push_back(
gfx::Point(event->pointer_data->x, event->pointer_data->y));
if (move_points_.size() > kMaxSize) {
// We need to pop in sets of three as the tail of the 'line' drawn
// with these move points is being drawn as a cubic path. Popping
// more or less than three results in the line oscillating as the path
// goes through points in a different way than was drawn before.
move_points_.pop_front();
move_points_.pop_front();
move_points_.pop_front();
}
}
break;
case mojo::EVENT_TYPE_POINTER_CANCEL:
case mojo::EVENT_TYPE_KEY_RELEASED:
case mojo::EVENT_TYPE_KEY_PRESSED:
case mojo::EVENT_TYPE_UNKNOWN:
default:
break;
}
// draw line
mojo::GaneshContext::Scope scope(gr_context_.get());
mojo::GaneshSurface surface(
gr_context_.get(),
make_scoped_ptr(new mojo::GLTexture(gl_context_, size)));
gr_context_.get()->gr()->resetContext(kTextureBinding_GrGLBackendState);
SkCanvas* canvas = surface.canvas();
DrawKeysToCanvas(size, canvas);
if (move_points_.size() > 1) {
DrawMovePointTrail(canvas);
}
if (last_key_ != nullptr && event->action != mojo::EVENT_TYPE_POINTER_UP) {
DrawFloatingKey(canvas, size, event->pointer_data->x,
event->pointer_data->y);
}
canvas->flush();
texture_uploader_->Upload(surface.TakeTexture());
}
void ViewObserverDelegate::DrawMovePointTrail(SkCanvas* canvas) {
std::deque<gfx::Point>::reverse_iterator it = move_points_.rbegin();
if (it != move_points_.rend()) {
SkPaint paint;
paint.setColor(SK_ColorBLUE);
paint.setStrokeWidth(15);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeCap(SkPaint::kRound_Cap);
paint.setStrokeJoin(SkPaint::kRound_Join);
SkPath path;
path.moveTo(static_cast<float>(it->x()), static_cast<float>(it->y()));
it++;
int extraPoints = ((move_points_.size() - 1) % 3);
switch (extraPoints) {
case 1: {
gfx::Point point1 = *it;
it++;
path.lineTo(static_cast<float>(point1.x()),
static_cast<float>(point1.y()));
} break;
case 2: {
gfx::Point point1 = *it;
it++;
gfx::Point point2 = *it;
it++;
path.quadTo(
static_cast<float>(point1.x()), static_cast<float>(point1.y()),
static_cast<float>(point2.x()), static_cast<float>(point2.y()));
} break;
}
// Remaining points are a multiple of 3 so we don't need to check for end
// each it++
while (it != move_points_.rend()) {
gfx::Point point1 = *it;
it++;
gfx::Point point2 = *it;
it++;
gfx::Point point3 = *it;
it++;
path.cubicTo(
static_cast<float>(point1.x()), static_cast<float>(point1.y()),
static_cast<float>(point2.x()), static_cast<float>(point2.y()),
static_cast<float>(point3.x()), static_cast<float>(point3.y()));
}
canvas->drawPath(path, paint);
}
}
void ViewObserverDelegate::DrawFloatingKey(SkCanvas* canvas,
const mojo::Size& size,
float current_touch_x,
float current_touch_y) {
SkPaint paint;
paint.setColor(SK_ColorYELLOW);
float row_height = static_cast<float>(size.height) / 4.0f;
float floating_key_width = static_cast<float>(size.width) / 7.0f;
float left = current_touch_x - (floating_key_width / 2);
float top = current_touch_y - (1.5f * row_height);
SkRect rect =
SkRect::MakeLTRB(left, top, left + floating_key_width, top + row_height);
canvas->drawRect(rect, paint);
skia::RefPtr<SkTypeface> typeface =
skia::AdoptRef(SkTypeface::CreateFromName("Arial", SkTypeface::kNormal));
SkPaint text_paint;
text_paint.setTypeface(typeface.get());
text_paint.setColor(SK_ColorBLACK);
text_paint.setTextSize(row_height / 1.7f);
text_paint.setAntiAlias(true);
text_paint.setTextAlign(SkPaint::kCenter_Align);
last_key_->Draw(canvas, text_paint,
gfx::RectF(left, top, floating_key_width, row_height));
}
// mojo::TextureUploader::Client implementation.
void ViewObserverDelegate::OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) {
view_->SetSurfaceId(surface_id.Pass());
}
// mojo::ViewObserver implementation.
void ViewObserverDelegate::OnViewBoundsChanged(mojo::View* view,
const mojo::Rect& old_bounds,
const mojo::Rect& new_bounds) {
DrawKeys(ToSize(new_bounds));
}
void ViewObserverDelegate::OnViewInputEvent(mojo::View* view,
const mojo::EventPtr& event) {
mojo::Size size = ToSize(view_->bounds());
key_layout_.SetSize(size);
if (event->pointer_data) {
gfx::Point point(event->pointer_data->x, event->pointer_data->y);
last_key_ = key_layout_.GetKeyAtPoint(point);
last_action_ = event->action;
if (event->action == mojo::EVENT_TYPE_POINTER_UP) {
key_layout_.OnTouchUp(point);
}
UpdateMovePoints(size, event);
}
}
void ViewObserverDelegate::OnViewDestroyed(mojo::View* view) {
if (view_ == view) {
view_->RemoveObserver(this);
view_ = nullptr;
gl_context_->Destroy();
}
}
} // namespace keyboard