blob: 88da751dd0ac55fed8534bf6a6072b09a5399079 [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/wm/frame_painter.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/property_util.h"
#include "base/memory/scoped_ptr.h"
#include "grit/ash_resources.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/root_window.h"
#include "ui/base/hit_test.h"
#include "ui/gfx/screen.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/non_client_view.h"
using views::Widget;
using views::ImageButton;
namespace {
class ResizableWidgetDelegate : public views::WidgetDelegate {
public:
ResizableWidgetDelegate(views::Widget* widget) {
widget_ = widget;
}
virtual bool CanResize() const OVERRIDE { return true; }
// Implementations of the widget class.
virtual views::Widget* GetWidget() OVERRIDE { return widget_; }
virtual const views::Widget* GetWidget() const OVERRIDE { return widget_; }
private:
views::Widget* widget_;
DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate);
};
// Creates a test widget that owns its native widget.
Widget* CreateTestWidget() {
Widget* widget = new Widget;
Widget::InitParams params;
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget->Init(params);
return widget;
}
Widget* CreateAlwaysOnTopWidget() {
Widget* widget = new Widget;
Widget::InitParams params;
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.keep_on_top = true;
widget->Init(params);
return widget;
}
Widget* CreateResizableWidget() {
Widget* widget = new Widget;
Widget::InitParams params;
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.keep_on_top = true;
params.delegate = new ResizableWidgetDelegate(widget);
params.type = Widget::InitParams::TYPE_WINDOW;
widget->Init(params);
return widget;
}
} // namespace
namespace ash {
typedef ash::test::AshTestBase FramePainterTest;
TEST_F(FramePainterTest, Basics) {
// Other tests might have created a FramePainter, so we cannot assert that
// FramePainter::instances_ is NULL here.
// Creating a painter bumps the instance count.
scoped_ptr<FramePainter> painter(new FramePainter);
ASSERT_TRUE(FramePainter::instances_);
EXPECT_EQ(1u, FramePainter::instances_->size());
// Destroying that painter leaves a valid pointer but no instances.
painter.reset();
ASSERT_TRUE(FramePainter::instances_);
EXPECT_EQ(0u, FramePainter::instances_->size());
}
TEST_F(FramePainterTest, UseSoloWindowHeader) {
// No windows, no solo window mode.
EXPECT_FALSE(FramePainter::UseSoloWindowHeader());
// Create a widget and a painter for it.
scoped_ptr<Widget> w1(CreateTestWidget());
FramePainter p1;
ImageButton size1(NULL);
ImageButton close1(NULL);
p1.Init(w1.get(), NULL, &size1, &close1, FramePainter::SIZE_BUTTON_MAXIMIZES);
w1->Show();
// We only have one window, so it should use a solo header.
EXPECT_TRUE(FramePainter::UseSoloWindowHeader());
// Create a second widget and painter.
scoped_ptr<Widget> w2(CreateTestWidget());
FramePainter p2;
ImageButton size2(NULL);
ImageButton close2(NULL);
p2.Init(w2.get(), NULL, &size2, &close2, FramePainter::SIZE_BUTTON_MAXIMIZES);
w2->Show();
// Now there are two windows, so we should not use solo headers.
EXPECT_FALSE(FramePainter::UseSoloWindowHeader());
// Hide one window. Solo should be enabled.
w2->Hide();
EXPECT_TRUE(FramePainter::UseSoloWindowHeader());
// Show that window. Solo should be disabled.
w2->Show();
EXPECT_FALSE(FramePainter::UseSoloWindowHeader());
// Minimize the window. Solo should be enabled.
w2->Minimize();
EXPECT_TRUE(FramePainter::UseSoloWindowHeader());
// Close the minimized window.
w2.reset();
EXPECT_TRUE(FramePainter::UseSoloWindowHeader());
// Open an always-on-top widget (which lives in a different container).
scoped_ptr<Widget> w3(CreateAlwaysOnTopWidget());
FramePainter p3;
ImageButton size3(NULL);
ImageButton close3(NULL);
p3.Init(w3.get(), NULL, &size3, &close3, FramePainter::SIZE_BUTTON_MAXIMIZES);
w3->Show();
EXPECT_FALSE(FramePainter::UseSoloWindowHeader());
// Close the always-on-top widget.
w3.reset();
EXPECT_TRUE(FramePainter::UseSoloWindowHeader());
// Close the first window.
w1.reset();
EXPECT_FALSE(FramePainter::UseSoloWindowHeader());
}
TEST_F(FramePainterTest, GetHeaderOpacity) {
// Create a widget and a painter for it.
scoped_ptr<Widget> w1(CreateTestWidget());
FramePainter p1;
ImageButton size1(NULL);
ImageButton close1(NULL);
p1.Init(w1.get(), NULL, &size1, &close1, FramePainter::SIZE_BUTTON_MAXIMIZES);
w1->Show();
// Solo active window has solo window opacity.
EXPECT_EQ(FramePainter::kSoloWindowOpacity,
p1.GetHeaderOpacity(FramePainter::ACTIVE,
IDR_AURA_WINDOW_HEADER_BASE_ACTIVE,
NULL));
// Create a second widget and painter.
scoped_ptr<Widget> w2(CreateTestWidget());
FramePainter p2;
ImageButton size2(NULL);
ImageButton close2(NULL);
p2.Init(w2.get(), NULL, &size2, &close2, FramePainter::SIZE_BUTTON_MAXIMIZES);
w2->Show();
// Active window has active window opacity.
EXPECT_EQ(FramePainter::kActiveWindowOpacity,
p2.GetHeaderOpacity(FramePainter::ACTIVE,
IDR_AURA_WINDOW_HEADER_BASE_ACTIVE,
NULL));
// Inactive window has inactive window opacity.
EXPECT_EQ(FramePainter::kInactiveWindowOpacity,
p2.GetHeaderOpacity(FramePainter::INACTIVE,
IDR_AURA_WINDOW_HEADER_BASE_INACTIVE,
NULL));
// Custom overlay image is drawn completely opaque.
gfx::ImageSkia custom_overlay;
EXPECT_EQ(255,
p1.GetHeaderOpacity(FramePainter::ACTIVE,
IDR_AURA_WINDOW_HEADER_BASE_ACTIVE,
&custom_overlay));
}
// Test the hit test function with windows which are "partially maximized".
TEST_F(FramePainterTest, HitTestSpecialMaximizedModes) {
// Create a widget and a painter for it.
scoped_ptr<Widget> w1(CreateResizableWidget());
FramePainter p1;
ImageButton size1(NULL);
ImageButton close1(NULL);
p1.Init(w1.get(), NULL, &size1, &close1, FramePainter::SIZE_BUTTON_MAXIMIZES);
views::NonClientFrameView* frame = w1->non_client_view()->frame_view();
w1->Show();
gfx::Rect any_rect = gfx::Rect(0, 0, 100, 100);
gfx::Rect screen = gfx::Screen::GetDisplayMatching(any_rect).work_area();
w1->SetBounds(any_rect);
EXPECT_EQ(HTTOPLEFT, p1.NonClientHitTest(frame, gfx::Point(0, 15)));
w1->SetBounds(gfx::Rect(
screen.x(), screen.y(), screen.width() / 2, screen.height()));
// A hit without a set restore rect should produce a top left hit.
EXPECT_EQ(HTTOPLEFT, p1.NonClientHitTest(frame, gfx::Point(0, 15)));
ash::SetRestoreBoundsInScreen(w1->GetNativeWindow(), any_rect);
// A hit into the corner should produce nowhere - not left.
EXPECT_EQ(HTCAPTION, p1.NonClientHitTest(frame, gfx::Point(0, 15)));
// A hit into the middle upper area should generate right - not top&right.
EXPECT_EQ(HTRIGHT,
p1.NonClientHitTest(frame, gfx::Point(screen.width() / 2, 15)));
// A hit into the middle should generate right.
EXPECT_EQ(HTRIGHT,
p1.NonClientHitTest(frame, gfx::Point(screen.width() / 2,
screen.height() / 2)));
// A hit into the middle lower area should generate right - not bottom&right.
EXPECT_EQ(HTRIGHT,
p1.NonClientHitTest(frame, gfx::Point(screen.width() / 2,
screen.height() - 1)));
}
} // namespace ash