blob: fb0aec27be85d3ebb552e4d66487cf390de78481 [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 "ash/touch/touch_observer_hud.h"
#include "ash/shell_window_ids.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkXfermode.h"
#include "ui/base/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/display.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size.h"
#include "ui/views/background.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace internal {
const int kMaxPaths = 15;
const int kScale = 10;
const int kColors[] = {
static_cast<int>(SK_ColorYELLOW),
static_cast<int>(SK_ColorGREEN),
static_cast<int>(SK_ColorRED),
static_cast<int>(SK_ColorBLUE),
static_cast<int>(SK_ColorMAGENTA),
static_cast<int>(SK_ColorCYAN),
static_cast<int>(SK_ColorWHITE),
static_cast<int>(SK_ColorBLACK)
};
class TouchHudCanvas : public views::View {
public:
explicit TouchHudCanvas(TouchObserverHUD* owner)
: owner_(owner),
path_index_(0),
color_index_(0) {
gfx::Display display = gfx::Screen::GetPrimaryDisplay();
gfx::Rect bounds = display.bounds();
size_.set_width(bounds.width() / kScale);
size_.set_height(bounds.height() / kScale);
}
virtual ~TouchHudCanvas() {}
void Start(int id, const gfx::Point& point) {
paths_[path_index_].reset();
paths_[path_index_].moveTo(SkIntToScalar(point.x() / kScale),
SkIntToScalar(point.y() / kScale));
colors_[path_index_] = kColors[color_index_];
color_index_ = (color_index_ + 1) % arraysize(kColors);
touch_id_to_path_[id] = path_index_;
path_index_ = (path_index_ + 1) % kMaxPaths;
SchedulePaint();
}
void Update(int id, gfx::Point& to) {
SkPoint last;
int path_id = touch_id_to_path_[id];
SkScalar x = SkIntToScalar(to.x() / kScale);
SkScalar y = SkIntToScalar(to.y() / kScale);
if (!paths_[path_id].getLastPt(&last) || x != last.x() || y != last.y())
paths_[path_id].lineTo(x, y);
SchedulePaint();
}
private:
// Overridden from views::View.
virtual gfx::Size GetPreferredSize() OVERRIDE {
return size_;
}
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
canvas->DrawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
canvas->DrawColor(SkColorSetARGB(25, 0, 0, 0));
SkPaint paint;
paint.setStrokeWidth(SkIntToScalar(2));
paint.setStyle(SkPaint::kStroke_Style);
for (size_t i = 0; i < arraysize(paths_); ++i) {
if (paths_[i].countPoints() == 0)
continue;
paint.setColor(colors_[i]);
if (paths_[i].countPoints() == 1) {
SkPoint point = paths_[i].getPoint(0);
canvas->sk_canvas()->drawPoint(point.x(), point.y(), paint);
} else {
canvas->DrawPath(paths_[i], paint);
}
}
}
TouchObserverHUD* owner_;
SkPath paths_[kMaxPaths];
SkColor colors_[kMaxPaths];
int path_index_;
int color_index_;
std::map<int, int> touch_id_to_path_;
gfx::Size size_;
DISALLOW_COPY_AND_ASSIGN(TouchHudCanvas);
};
TouchObserverHUD::TouchObserverHUD() {
views::View* content = new views::View;
content->SetLayoutManager(new views::BoxLayout(
views::BoxLayout::kVertical, 0, 0, 0));
canvas_ = new TouchHudCanvas(this);
content->AddChildView(canvas_);
for (int i = 0; i < kMaxTouchPoints; ++i) {
touch_status_[i] = ui::ET_UNKNOWN;
touch_labels_[i] = new views::Label;
touch_labels_[i]->SetBackgroundColor(SkColorSetARGB(0, 255, 255, 255));
touch_labels_[i]->SetShadowColors(SK_ColorWHITE,
SK_ColorWHITE);
touch_labels_[i]->SetShadowOffset(1, 1);
touch_labels_[i]->SetVisible(false);
content->AddChildView(touch_labels_[i]);
}
widget_ = new views::Widget();
views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.transparent = true;
params.can_activate = false;
params.accept_events = false;
params.bounds = gfx::Rect(content->GetPreferredSize());
params.parent = Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_OverlayContainer);
widget_->Init(params);
widget_->SetContentsView(content);
widget_->StackAtTop();
widget_->Show();
// The TouchObserverHUD's lifetime is always more than |widget_|. The
// |widget_| is unset from the OnWidgetClosing callback.
widget_->AddObserver(this);
}
TouchObserverHUD::~TouchObserverHUD() {
// The widget should have already been destroyed.
DCHECK(!widget_);
}
void TouchObserverHUD::UpdateTouchPointLabel(int index) {
const char* status = NULL;
switch (touch_status_[index]) {
case ui::ET_UNKNOWN:
status = " ";
break;
case ui::ET_TOUCH_PRESSED:
status = "P";
break;
case ui::ET_TOUCH_MOVED:
status = "M";
break;
case ui::ET_TOUCH_RELEASED:
status = "R";
break;
case ui::ET_TOUCH_CANCELLED:
status = "C";
break;
default:
status = "?";
break;
}
std::string string = base::StringPrintf("%2d: %s %s",
index, status, touch_positions_[index].ToString().c_str());
touch_labels_[index]->SetText(UTF8ToUTF16(string));
}
bool TouchObserverHUD::PreHandleKeyEvent(aura::Window* target,
ui::KeyEvent* event) {
return false;
}
bool TouchObserverHUD::PreHandleMouseEvent(aura::Window* target,
ui::MouseEvent* event) {
return false;
}
ui::TouchStatus TouchObserverHUD::PreHandleTouchEvent(
aura::Window* target,
ui::TouchEvent* event) {
if (event->touch_id() >= kMaxTouchPoints)
return ui::TOUCH_STATUS_UNKNOWN;
if (event->type() != ui::ET_TOUCH_CANCELLED)
touch_positions_[event->touch_id()] = event->root_location();
if (event->type() == ui::ET_TOUCH_PRESSED)
canvas_->Start(event->touch_id(), touch_positions_[event->touch_id()]);
else
canvas_->Update(event->touch_id(), touch_positions_[event->touch_id()]);
touch_status_[event->touch_id()] = event->type();
touch_labels_[event->touch_id()]->SetVisible(true);
UpdateTouchPointLabel(event->touch_id());
widget_->SetSize(widget_->GetContentsView()->GetPreferredSize());
return ui::TOUCH_STATUS_UNKNOWN;
}
ui::EventResult TouchObserverHUD::PreHandleGestureEvent(
aura::Window* target,
ui::GestureEvent* event) {
return ui::ER_UNHANDLED;
}
void TouchObserverHUD::OnWidgetClosing(views::Widget* widget) {
DCHECK_EQ(widget, widget_);
widget_ = NULL;
}
} // namespace internal
} // namespace ash