summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/shell/panel_window.cc2
-rw-r--r--ash/wm/panel_frame_view.cc33
-rw-r--r--ash/wm/panel_frame_view.h9
-rw-r--r--ash/wm/panel_layout_manager.cc62
-rw-r--r--ash/wm/panel_layout_manager.h10
-rw-r--r--ash/wm/panel_layout_manager_unittest.cc25
-rw-r--r--chrome/browser/extensions/api/app_window/app_window_api.cc15
-rw-r--r--chrome/browser/ui/ash/launcher/shell_window_launcher_controller.cc27
-rw-r--r--chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.mm2
-rw-r--r--chrome/browser/ui/extensions/shell_window.cc19
-rw-r--r--chrome/browser/ui/extensions/shell_window.h19
-rw-r--r--chrome/browser/ui/gtk/extensions/native_app_window_gtk.cc2
-rw-r--r--chrome/browser/ui/views/extensions/native_app_window_views.cc678
-rw-r--r--chrome/browser/ui/views/extensions/native_app_window_views.h57
-rw-r--r--chrome/browser/ui/views/extensions/shell_window_frame_view.cc348
-rw-r--r--chrome/browser/ui/views/extensions/shell_window_frame_view.h77
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--chrome/common/extensions/api/app_window.idl4
18 files changed, 818 insertions, 573 deletions
diff --git a/ash/shell/panel_window.cc b/ash/shell/panel_window.cc
index e5d68ac..fb70024 100644
--- a/ash/shell/panel_window.cc
+++ b/ash/shell/panel_window.cc
@@ -76,7 +76,7 @@ bool PanelWindow::CanMaximize() const {
views::NonClientFrameView* PanelWindow::CreateNonClientFrameView(
views::Widget* widget) {
- return new PanelFrameView(widget);
+ return new PanelFrameView(widget, PanelFrameView::FRAME_NONE);
}
} // namespace ash
diff --git a/ash/wm/panel_frame_view.cc b/ash/wm/panel_frame_view.cc
index 4ca9499..52a53c5 100644
--- a/ash/wm/panel_frame_view.cc
+++ b/ash/wm/panel_frame_view.cc
@@ -2,8 +2,9 @@
// 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/wm/panel_frame_view.h"
+
+#include "ash/wm/frame_painter.h"
#include "grit/ash_resources.h"
#include "grit/ui_strings.h" // Accessibility names
#include "third_party/skia/include/core/SkPaint.h"
@@ -22,8 +23,17 @@
namespace ash {
-PanelFrameView::PanelFrameView(views::Widget* frame)
- : frame_painter_(new FramePainter) {
+PanelFrameView::PanelFrameView(views::Widget* frame, FrameType frame_type) {
+ if (frame_type != FRAME_NONE)
+ InitFramePainter(frame);
+}
+
+PanelFrameView::~PanelFrameView() {
+}
+
+void PanelFrameView::InitFramePainter(views::Widget* frame) {
+ frame_painter_.reset(new FramePainter);
+
close_button_ = new views::ImageButton(this);
close_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
@@ -38,10 +48,9 @@ PanelFrameView::PanelFrameView(views::Widget* frame)
FramePainter::SIZE_BUTTON_MINIMIZES);
}
-PanelFrameView::~PanelFrameView() {
-}
-
void PanelFrameView::Layout() {
+ if (!frame_painter_.get())
+ return;
frame_painter_->LayoutHeader(this, true);
}
@@ -54,7 +63,7 @@ void PanelFrameView::UpdateWindowIcon() {
}
void PanelFrameView::UpdateWindowTitle() {
- NOTIMPLEMENTED();
+ // TODO(stevenjb): Support titles for panels?
}
void PanelFrameView::GetWindowMask(const gfx::Size&, gfx::Path*) {
@@ -62,10 +71,14 @@ void PanelFrameView::GetWindowMask(const gfx::Size&, gfx::Path*) {
}
int PanelFrameView::NonClientHitTest(const gfx::Point& point) {
+ if (!frame_painter_.get())
+ return HTNOWHERE;
return frame_painter_->NonClientHitTest(this, point);
}
void PanelFrameView::OnPaint(gfx::Canvas* canvas) {
+ if (!frame_painter_.get())
+ return;
bool paint_as_active = ShouldPaintAsActive();
int theme_image_id = paint_as_active ? IDR_AURA_WINDOW_HEADER_BASE_ACTIVE :
IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
@@ -79,6 +92,8 @@ void PanelFrameView::OnPaint(gfx::Canvas* canvas) {
}
gfx::Rect PanelFrameView::GetBoundsForClientView() const {
+ if (!frame_painter_.get())
+ return bounds();
return frame_painter_->GetBoundsForClientView(
close_button_->bounds().bottom(),
bounds());
@@ -86,6 +101,8 @@ gfx::Rect PanelFrameView::GetBoundsForClientView() const {
gfx::Rect PanelFrameView::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const {
+ if (!frame_painter_.get())
+ return client_bounds;
return frame_painter_->GetWindowBoundsForClientBounds(
close_button_->bounds().bottom(), client_bounds);
}
@@ -94,6 +111,8 @@ void PanelFrameView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
if (sender == close_button_)
GetWidget()->Close();
+ if (sender == minimize_button_)
+ GetWidget()->Minimize();
}
} // namespace ash
diff --git a/ash/wm/panel_frame_view.h b/ash/wm/panel_frame_view.h
index 20ab18b..248eec5 100644
--- a/ash/wm/panel_frame_view.h
+++ b/ash/wm/panel_frame_view.h
@@ -22,10 +22,17 @@ class FramePainter;
class ASH_EXPORT PanelFrameView : public views::NonClientFrameView,
public views::ButtonListener {
public:
- explicit PanelFrameView(views::Widget* frame);
+ enum FrameType {
+ FRAME_NONE,
+ FRAME_ASH
+ };
+
+ PanelFrameView(views::Widget* frame, FrameType frame_type);
virtual ~PanelFrameView();
private:
+ void InitFramePainter(views::Widget* frame);
+
// Overridden from views::NonClientFrameView:
virtual gfx::Rect GetBoundsForClientView() const OVERRIDE;
virtual gfx::Rect GetWindowBoundsForClientBounds(
diff --git a/ash/wm/panel_layout_manager.cc b/ash/wm/panel_layout_manager.cc
index 3f95a19..ab3f8ee 100644
--- a/ash/wm/panel_layout_manager.cc
+++ b/ash/wm/panel_layout_manager.cc
@@ -11,6 +11,8 @@
#include "ash/shell.h"
#include "ash/wm/frame_painter.h"
#include "ash/wm/property_util.h"
+#include "ash/wm/window_animations.h"
+#include "ash/wm/window_util.h"
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -19,6 +21,7 @@
#include "third_party/skia/include/core/SkPath.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/focus_manager.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/gfx/canvas.h"
@@ -34,8 +37,6 @@ const int kPanelMarginEdge = 4;
const int kPanelMarginMiddle = 8;
const int kPanelIdealSpacing = 4;
-const int kMinimizedHeight = 24;
-
const float kMaxHeightFactor = .80f;
const float kMaxWidthFactor = .50f;
@@ -159,6 +160,7 @@ void PanelLayoutManager::StartDragging(aura::Window* panel) {
DCHECK(!dragged_panel_);
DCHECK(panel->parent() == panel_container_);
dragged_panel_ = panel;
+ Relayout();
}
void PanelLayoutManager::FinishDragging() {
@@ -176,28 +178,10 @@ void PanelLayoutManager::ToggleMinimize(aura::Window* panel) {
DCHECK(panel->parent() == panel_container_);
if (panel->GetProperty(aura::client::kShowStateKey) ==
ui::SHOW_STATE_MINIMIZED) {
- const gfx::Rect& old_bounds = panel->bounds();
panel->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
-
- gfx::Rect new_bounds(old_bounds);
- const gfx::Rect* restore_bounds = GetRestoreBoundsInScreen(panel);
- if (restore_bounds) {
- new_bounds.set_height(restore_bounds->height());
- new_bounds.set_y(old_bounds.bottom() - restore_bounds->height());
- SetChildBounds(panel, new_bounds);
- ClearRestoreBounds(panel);
- }
} else {
- const gfx::Rect& old_bounds = panel->bounds();
panel->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
- SetRestoreBoundsInParent(panel, old_bounds);
- SetChildBounds(panel,
- gfx::Rect(old_bounds.x(),
- old_bounds.bottom() - kMinimizedHeight,
- old_bounds.width(),
- kMinimizedHeight));
}
- Relayout();
}
////////////////////////////////////////////////////////////////////////////////
@@ -210,6 +194,7 @@ void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
if (child == callout_widget_->GetNativeWindow())
return;
panel_windows_.push_back(child);
+ child->AddObserver(this);
Relayout();
}
@@ -218,6 +203,7 @@ void PanelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
std::find(panel_windows_.begin(), panel_windows_.end(), child);
if (found != panel_windows_.end())
panel_windows_.erase(found);
+ child->RemoveObserver(this);
if (dragged_panel_ == child)
dragged_panel_ = NULL;
@@ -279,6 +265,22 @@ void PanelLayoutManager::OnLauncherIconPositionsChanged() {
Relayout();
}
+/////////////////////////////////////////////////////////////////////////////
+// PanelLayoutManager, WindowObserver implementation:
+
+void PanelLayoutManager::OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) {
+ if (key != aura::client::kShowStateKey)
+ return;
+ ui::WindowShowState new_state =
+ window->GetProperty(aura::client::kShowStateKey);
+ if (new_state == ui::SHOW_STATE_MINIMIZED)
+ MinimizePanel(window);
+ else
+ RestorePanel(window);
+}
+
////////////////////////////////////////////////////////////////////////////////
// PanelLayoutManager, aura::client::ActivationChangeObserver implementation:
void PanelLayoutManager::OnWindowActivated(aura::Window* active,
@@ -291,9 +293,23 @@ void PanelLayoutManager::OnWindowActivated(aura::Window* active,
}
}
-
////////////////////////////////////////////////////////////////////////////////
// PanelLayoutManager private implementation:
+
+void PanelLayoutManager::MinimizePanel(aura::Window* panel) {
+ views::corewm::SetWindowVisibilityAnimationType(
+ panel, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
+ panel->Hide();
+ if (wm::IsActiveWindow(panel))
+ wm::DeactivateWindow(panel);
+ Relayout();
+}
+
+void PanelLayoutManager::RestorePanel(aura::Window* panel) {
+ panel->Show();
+ Relayout();
+}
+
void PanelLayoutManager::Relayout() {
if (!launcher_ || !launcher_->widget())
return;
@@ -324,7 +340,9 @@ void PanelLayoutManager::Relayout() {
if (icon_bounds.IsEmpty())
continue;
- if (panel->HasFocus()) {
+ if (panel->HasFocus() ||
+ panel->Contains(
+ aura::client::GetFocusClient(panel)->GetFocusedWindow())) {
DCHECK(!active_panel);
active_panel = panel;
}
diff --git a/ash/wm/panel_layout_manager.h b/ash/wm/panel_layout_manager.h
index 2e41521..1b68378 100644
--- a/ash/wm/panel_layout_manager.h
+++ b/ash/wm/panel_layout_manager.h
@@ -15,6 +15,7 @@
#include "base/memory/weak_ptr.h"
#include "ui/aura/client/activation_change_observer.h"
#include "ui/aura/layout_manager.h"
+#include "ui/aura/window_observer.h"
namespace aura {
class Window;
@@ -45,6 +46,7 @@ namespace internal {
class ASH_EXPORT PanelLayoutManager
: public aura::LayoutManager,
public ash::LauncherIconObserver,
+ public aura::WindowObserver,
public aura::client::ActivationChangeObserver {
public:
explicit PanelLayoutManager(aura::Window* panel_container);
@@ -70,6 +72,11 @@ class ASH_EXPORT PanelLayoutManager
// Overridden from ash::LauncherIconObserver
virtual void OnLauncherIconPositionsChanged() OVERRIDE;
+ // Overridden from aura::WindowObserver
+ virtual void OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) OVERRIDE;
+
// Overridden from aura::client::ActivationChangeObserver
virtual void OnWindowActivated(aura::Window* active,
aura::Window* old_active) OVERRIDE;
@@ -79,6 +86,9 @@ class ASH_EXPORT PanelLayoutManager
typedef std::list<aura::Window*> PanelList;
+ void MinimizePanel(aura::Window* panel);
+ void RestorePanel(aura::Window* panel);
+
// Called whenever the panel layout might change.
void Relayout();
diff --git a/ash/wm/panel_layout_manager_unittest.cc b/ash/wm/panel_layout_manager_unittest.cc
index 424fe0c..bbbbc92 100644
--- a/ash/wm/panel_layout_manager_unittest.cc
+++ b/ash/wm/panel_layout_manager_unittest.cc
@@ -15,8 +15,9 @@
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
-#include "ui/aura/window.h"
+#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
#include "ui/views/widget/widget.h"
namespace ash {
@@ -297,5 +298,27 @@ TEST_F(PanelLayoutManagerTest, FanWindows) {
EXPECT_GT(spacing, icon_x2 - icon_x1);
}
+TEST_F(PanelLayoutManagerTest, MinimizeRestorePanel) {
+ gfx::Rect bounds(0, 0, 201, 201);
+ scoped_ptr<aura::Window> window(CreatePanelWindow(bounds));
+ // Activate the window, ensure callout is visible.
+ wm::ActivateWindow(window.get());
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsCalloutVisible());
+ // Minimize the panel, callout should be hidden.
+ window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(IsCalloutVisible());
+ // Restore the pantel; panel should not be activated by default and callout
+ // should be hidden.
+ window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(IsCalloutVisible());
+ // Activate the window, ensure callout is visible.
+ wm::ActivateWindow(window.get());
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsCalloutVisible());
+}
+
} // namespace internal
} // namespace ash
diff --git a/chrome/browser/extensions/api/app_window/app_window_api.cc b/chrome/browser/extensions/api/app_window/app_window_api.cc
index 9df135c..ee40b6c 100644
--- a/chrome/browser/extensions/api/app_window/app_window_api.cc
+++ b/chrome/browser/extensions/api/app_window/app_window_api.cc
@@ -33,6 +33,7 @@ const char kInvalidWindowId[] =
"The window id can not be more than 256 characters long.";
}
+const char kPanelTypeOption[] = "panel";
const char kNoneFrameOption[] = "none";
const char kHtmlFrameOption[] = "experimental-html";
@@ -144,16 +145,24 @@ bool AppWindowCreateFunction::RunImpl() {
create_params.bounds.set_y(*bounds->top.get());
}
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalExtensionApis)) {
+ if (options->type.get()) {
+ if (*options->type == kPanelTypeOption)
+ create_params.window_type = ShellWindow::WINDOW_TYPE_PANEL;
+ }
+ }
+
if (options->frame.get()) {
if (*options->frame == kHtmlFrameOption &&
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExperimentalExtensionApis)) {
- create_params.frame = ShellWindow::CreateParams::FRAME_NONE;
+ create_params.frame = ShellWindow::FRAME_NONE;
inject_html_titlebar = true;
} else if (*options->frame == kNoneFrameOption) {
- create_params.frame = ShellWindow::CreateParams::FRAME_NONE;
+ create_params.frame = ShellWindow::FRAME_NONE;
} else {
- create_params.frame = ShellWindow::CreateParams::FRAME_CHROME;
+ create_params.frame = ShellWindow::FRAME_CHROME;
}
}
diff --git a/chrome/browser/ui/ash/launcher/shell_window_launcher_controller.cc b/chrome/browser/ui/ash/launcher/shell_window_launcher_controller.cc
index af2b06c..a55016b 100644
--- a/chrome/browser/ui/ash/launcher/shell_window_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/shell_window_launcher_controller.cc
@@ -9,6 +9,8 @@
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "base/stl_util.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
@@ -19,17 +21,12 @@
namespace {
-// Currently apps have a single launcher item, so the launcher id is the
-// same as the app id. In the future, this may not be true (e.g. for panels).
std::string GetAppLauncherId(ShellWindow* shell_window) {
+ if (shell_window->window_type() == ShellWindow::WINDOW_TYPE_PANEL)
+ return StringPrintf("panel:%d", shell_window->session_id().id());
return shell_window->extension()->id();
}
-bool AppLauncherIdIsForApp(const std::string& app_launcher_id,
- const std::string& app_id) {
- return app_launcher_id == app_id;
-}
-
// Functor for std::find_if used in AppLauncherItemController.
class ShellWindowHasWindow {
public:
@@ -114,7 +111,7 @@ class ShellWindowLauncherController::AppLauncherItemController
virtual void Activate() OVERRIDE {
DCHECK(!shell_windows_.empty());
- ShowAndActivate(shell_windows_.front());
+ shell_windows_.front()->GetBaseWindow()->Activate();
}
virtual void Close() OVERRIDE {
@@ -139,14 +136,14 @@ class ShellWindowLauncherController::AppLauncherItemController
if (first_window->GetBaseWindow()->IsActive())
first_window->GetBaseWindow()->Minimize();
else
- ShowAndActivate(first_window);
+ RestoreOrShow(first_window);
} else {
if (!first_window->GetBaseWindow()->IsActive()) {
- ShowAndActivate(first_window);
+ RestoreOrShow(first_window);
} else {
shell_windows_.pop_front();
shell_windows_.push_back(first_window);
- ShowAndActivate(shell_windows_.front());
+ RestoreOrShow(shell_windows_.front());
}
}
}
@@ -163,8 +160,12 @@ class ShellWindowLauncherController::AppLauncherItemController
private:
typedef std::list<ShellWindow*> ShellWindowList;
- void ShowAndActivate(ShellWindow* shell_window) {
- shell_window->GetBaseWindow()->Show();
+ void RestoreOrShow(ShellWindow* shell_window) {
+ if (shell_window->GetBaseWindow()->IsMinimized())
+ shell_window->GetBaseWindow()->Restore();
+ else
+ shell_window->GetBaseWindow()->Show();
+ // Always activate windows when shown from the launcher.
shell_window->GetBaseWindow()->Activate();
}
diff --git a/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.mm
index 694ed54..9275ab6 100644
--- a/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/extensions/native_app_window_cocoa.mm
@@ -190,7 +190,7 @@ NativeAppWindowCocoa::NativeAppWindowCocoa(
ShellWindow* shell_window,
const ShellWindow::CreateParams& params)
: shell_window_(shell_window),
- has_frame_(params.frame == ShellWindow::CreateParams::FRAME_CHROME),
+ has_frame_(params.frame == ShellWindow::FRAME_CHROME),
attention_request_id_(0),
use_system_drag_(true) {
// Flip coordinates based on the primary screen.
diff --git a/chrome/browser/ui/extensions/shell_window.cc b/chrome/browser/ui/extensions/shell_window.cc
index 1eb23bb..32f0a1e 100644
--- a/chrome/browser/ui/extensions/shell_window.cc
+++ b/chrome/browser/ui/extensions/shell_window.cc
@@ -74,7 +74,8 @@ void SuspendRenderViewHost(RenderViewHost* rvh) {
} // namespace
ShellWindow::CreateParams::CreateParams()
- : frame(ShellWindow::CreateParams::FRAME_CHROME),
+ : window_type(ShellWindow::WINDOW_TYPE_DEFAULT),
+ frame(ShellWindow::FRAME_CHROME),
bounds(INT_MIN, INT_MIN, INT_MIN, INT_MIN),
creator_process_id(0), hidden(false) {
}
@@ -85,7 +86,7 @@ ShellWindow::CreateParams::~CreateParams() {
ShellWindow* ShellWindow::Create(Profile* profile,
const extensions::Extension* extension,
const GURL& url,
- const ShellWindow::CreateParams& params) {
+ const CreateParams& params) {
// This object will delete itself when the window is closed.
ShellWindow* window = new ShellWindow(profile, extension);
window->Init(url, params);
@@ -98,12 +99,15 @@ ShellWindow::ShellWindow(Profile* profile,
: profile_(profile),
extension_(extension),
web_contents_(NULL),
+ window_type_(WINDOW_TYPE_DEFAULT),
ALLOW_THIS_IN_INITIALIZER_LIST(
extension_function_dispatcher_(profile, this)) {
}
void ShellWindow::Init(const GURL& url,
const ShellWindow::CreateParams& params) {
+ window_type_ = params.window_type;
+
web_contents_.reset(WebContents::Create(
profile(), SiteInstance::CreateForURL(profile(), url), MSG_ROUTING_NONE,
NULL));
@@ -167,8 +171,12 @@ void ShellWindow::Init(const GURL& url,
native_app_window_.reset(NativeAppWindow::Create(this, new_params));
OnNativeWindowChanged();
- if (!params.hidden)
- GetBaseWindow()->Show();
+ if (!params.hidden) {
+ if (params.window_type == WINDOW_TYPE_PANEL)
+ GetBaseWindow()->ShowInactive(); // Panels are not activated by default.
+ else
+ GetBaseWindow()->Show();
+ }
// If the new view is in the same process as the creator, block the created
// RVH from loading anything until the background page has had a chance to do
@@ -345,7 +353,8 @@ string16 ShellWindow::GetTitle() const {
// WebContents::GetTitle() will return the page's URL if there's no <title>
// specified. However, we'd prefer to show the name of the extension in that
// case, so we directly inspect the NavigationEntry's title.
- if (!web_contents()->GetController().GetActiveEntry() ||
+ if (!web_contents() ||
+ !web_contents()->GetController().GetActiveEntry() ||
web_contents()->GetController().GetActiveEntry()->GetTitle().empty())
return UTF8ToUTF16(extension()->name());
string16 title = web_contents()->GetTitle();
diff --git a/chrome/browser/ui/extensions/shell_window.h b/chrome/browser/ui/extensions/shell_window.h
index 0a7a43f..31f639e 100644
--- a/chrome/browser/ui/extensions/shell_window.h
+++ b/chrome/browser/ui/extensions/shell_window.h
@@ -44,16 +44,23 @@ class ShellWindow : public content::NotificationObserver,
public ImageLoadingTracker::Observer,
public extensions::ExtensionKeybindingRegistry::Delegate {
public:
- struct CreateParams {
- enum Frame {
- FRAME_CHROME, // Chrome-style window frame.
- FRAME_NONE, // Frameless window.
- };
+ enum WindowType {
+ WINDOW_TYPE_DEFAULT, // Default shell window
+ WINDOW_TYPE_PANEL, // OS controlled panel window (Ash only)
+ };
+
+ enum Frame {
+ FRAME_CHROME, // Chrome-style window frame.
+ FRAME_NONE, // Frameless window.
+ };
+ struct CreateParams {
CreateParams();
~CreateParams();
+ WindowType window_type;
Frame frame;
+
// Specify the initial bounds of the window. INT_MIN designates
// 'unspecified' for any coordinate, and should be replaced with a default
// value.
@@ -85,6 +92,7 @@ class ShellWindow : public content::NotificationObserver,
const SessionID& session_id() const { return session_id_; }
const extensions::Extension* extension() const { return extension_; }
content::WebContents* web_contents() const { return web_contents_.get(); }
+ WindowType window_type() const { return window_type_; }
Profile* profile() const { return profile_; }
const gfx::Image& app_icon() const { return app_icon_; }
@@ -202,6 +210,7 @@ class ShellWindow : public content::NotificationObserver,
const SessionID session_id_;
scoped_ptr<content::WebContents> web_contents_;
+ WindowType window_type_;
content::NotificationRegistrar registrar_;
ExtensionFunctionDispatcher extension_function_dispatcher_;
diff --git a/chrome/browser/ui/gtk/extensions/native_app_window_gtk.cc b/chrome/browser/ui/gtk/extensions/native_app_window_gtk.cc
index 1c1dfa3..d0e243d 100644
--- a/chrome/browser/ui/gtk/extensions/native_app_window_gtk.cc
+++ b/chrome/browser/ui/gtk/extensions/native_app_window_gtk.cc
@@ -34,7 +34,7 @@ NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window,
state_(GDK_WINDOW_STATE_WITHDRAWN),
is_active_(false),
content_thinks_its_fullscreen_(false),
- frameless_(params.frame == ShellWindow::CreateParams::FRAME_NONE) {
+ frameless_(params.frame == ShellWindow::FRAME_NONE) {
window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gfx::NativeView native_view =
diff --git a/chrome/browser/ui/views/extensions/native_app_window_views.cc b/chrome/browser/ui/views/extensions/native_app_window_views.cc
index df16fd4..1f7f7c4 100644
--- a/chrome/browser/ui/views/extensions/native_app_window_views.cc
+++ b/chrome/browser/ui/views/extensions/native_app_window_views.cc
@@ -4,421 +4,91 @@
#include "chrome/browser/ui/views/extensions/native_app_window_views.h"
-#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
+#include "chrome/browser/ui/views/extensions/shell_window_frame_view.h"
#include "chrome/common/extensions/draggable_region.h"
#include "chrome/common/extensions/extension.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
-#include "grit/theme_resources.h"
-#include "grit/ui_strings.h" // Accessibility names
-#include "third_party/skia/include/core/SkPaint.h"
-#include "ui/base/hit_test.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/path.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/webview/webview.h"
-#include "ui/views/layout/grid_layout.h"
-#include "ui/views/views_delegate.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/non_client_view.h"
#if defined(OS_WIN) && !defined(USE_AURA)
+#include "base/utf_string_conversions.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/web_applications/web_app.h"
#include "ui/base/win/shell.h"
#endif
#if defined(USE_ASH)
-#include "ash/ash_constants.h"
#include "ash/wm/custom_frame_view_ash.h"
+#include "ash/wm/panel_frame_view.h"
#include "chrome/browser/ui/ash/ash_util.h"
-#include "ui/aura/env.h"
-#include "ui/aura/window.h"
#endif
namespace {
+const int kMinPanelWidth = 100;
+const int kMinPanelHeight = 100;
+const int kDefaultPanelWidth = 200;
+const int kDefaultPanelHeight = 300;
const int kResizeInsideBoundsSize = 5;
-const int kResizeAreaCornerSize = 16;
-
-// Height of the chrome-style caption, in pixels.
-const int kCaptionHeight = 25;
-} // namespace
-
-class ShellWindowFrameView : public views::NonClientFrameView,
- public views::ButtonListener {
- public:
- static const char kViewClassName[];
-
- explicit ShellWindowFrameView(NativeAppWindowViews* window);
- virtual ~ShellWindowFrameView();
-
- void Init(views::Widget* frame);
-
- // views::NonClientFrameView implementation.
- virtual gfx::Rect GetBoundsForClientView() const OVERRIDE;
- virtual gfx::Rect GetWindowBoundsForClientBounds(
- const gfx::Rect& client_bounds) const OVERRIDE;
- virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE;
- virtual void GetWindowMask(const gfx::Size& size,
- gfx::Path* window_mask) OVERRIDE;
- virtual void ResetWindowControls() OVERRIDE {}
- virtual void UpdateWindowIcon() OVERRIDE {}
- virtual void UpdateWindowTitle() OVERRIDE {}
-
- // views::View implementation.
- virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual void Layout() OVERRIDE;
- virtual std::string GetClassName() const OVERRIDE;
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
- virtual gfx::Size GetMinimumSize() OVERRIDE;
- virtual gfx::Size GetMaximumSize() OVERRIDE;
-
- private:
- // views::ButtonListener implementation.
- virtual void ButtonPressed(views::Button* sender, const ui::Event& event)
- OVERRIDE;
-
- NativeAppWindowViews* window_;
- views::Widget* frame_;
- views::ImageButton* close_button_;
- views::ImageButton* maximize_button_;
- views::ImageButton* restore_button_;
- views::ImageButton* minimize_button_;
-
- DISALLOW_COPY_AND_ASSIGN(ShellWindowFrameView);
-};
-
-const char ShellWindowFrameView::kViewClassName[] =
- "browser/ui/views/extensions/ShellWindowFrameView";
-
-ShellWindowFrameView::ShellWindowFrameView(NativeAppWindowViews* window)
- : window_(window),
- frame_(NULL),
- close_button_(NULL) {
-}
-
-ShellWindowFrameView::~ShellWindowFrameView() {
-}
-
-void ShellWindowFrameView::Init(views::Widget* frame) {
- frame_ = frame;
-
- if (!window_->frameless()) {
- ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
- close_button_ = new views::ImageButton(this);
- close_button_->SetImage(views::CustomButton::STATE_NORMAL,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE).ToImageSkia());
- close_button_->SetImage(views::CustomButton::STATE_HOVERED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_H).ToImageSkia());
- close_button_->SetImage(views::CustomButton::STATE_PRESSED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_P).ToImageSkia());
- close_button_->SetAccessibleName(
- l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
- AddChildView(close_button_);
- maximize_button_ = new views::ImageButton(this);
- maximize_button_->SetImage(views::CustomButton::STATE_NORMAL,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE).ToImageSkia());
- maximize_button_->SetImage(views::CustomButton::STATE_HOVERED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_H).ToImageSkia());
- maximize_button_->SetImage(views::CustomButton::STATE_PRESSED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_P).ToImageSkia());
- maximize_button_->SetImage(views::CustomButton::STATE_DISABLED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_D).ToImageSkia());
- maximize_button_->SetAccessibleName(
- l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
- AddChildView(maximize_button_);
- restore_button_ = new views::ImageButton(this);
- restore_button_->SetImage(views::CustomButton::STATE_NORMAL,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE).ToImageSkia());
- restore_button_->SetImage(views::CustomButton::STATE_HOVERED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_H).ToImageSkia());
- restore_button_->SetImage(views::CustomButton::STATE_PRESSED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_P).ToImageSkia());
- restore_button_->SetAccessibleName(
- l10n_util::GetStringUTF16(IDS_APP_ACCNAME_RESTORE));
- AddChildView(restore_button_);
- minimize_button_ = new views::ImageButton(this);
- minimize_button_->SetImage(views::CustomButton::STATE_NORMAL,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE).ToImageSkia());
- minimize_button_->SetImage(views::CustomButton::STATE_HOVERED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_H).ToImageSkia());
- minimize_button_->SetImage(views::CustomButton::STATE_PRESSED,
- rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_P).ToImageSkia());
- minimize_button_->SetAccessibleName(
- l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
- AddChildView(minimize_button_);
- }
-
-#if defined(USE_ASH)
- aura::Window* window = frame->GetNativeWindow();
- if (chrome::IsNativeWindowInAsh(window)) {
- // Ensure we get resize cursors for a few pixels outside our bounds.
- window->SetHitTestBoundsOverrideOuter(
- gfx::Insets(-ash::kResizeOutsideBoundsSize,
- -ash::kResizeOutsideBoundsSize,
- -ash::kResizeOutsideBoundsSize,
- -ash::kResizeOutsideBoundsSize),
- ash::kResizeOutsideBoundsScaleForTouch);
- // Ensure we get resize cursors just inside our bounds as well.
- // TODO(jeremya): do we need to update these when in fullscreen/maximized?
- window->set_hit_test_bounds_override_inner(
- gfx::Insets(ash::kResizeInsideBoundsSize, ash::kResizeInsideBoundsSize,
- ash::kResizeInsideBoundsSize,
- ash::kResizeInsideBoundsSize));
- }
-#endif
-}
-
-gfx::Rect ShellWindowFrameView::GetBoundsForClientView() const {
- if (window_->frameless() || frame_->IsFullscreen())
- return bounds();
- return gfx::Rect(0, kCaptionHeight, width(),
- std::max(0, height() - kCaptionHeight));
}
-gfx::Rect ShellWindowFrameView::GetWindowBoundsForClientBounds(
- const gfx::Rect& client_bounds) const {
- if (window_->frameless()) {
- gfx::Rect window_bounds = client_bounds;
- // Enforce minimum size (1, 1) in case that client_bounds is passed with
- // empty size. This could occur when the frameless window is being
- // initialized.
- if (window_bounds.IsEmpty()) {
- window_bounds.set_width(1);
- window_bounds.set_height(1);
- }
- return window_bounds;
- }
-
- int closeButtonOffsetX =
- (kCaptionHeight - close_button_->height()) / 2;
- int header_width = close_button_->width() + closeButtonOffsetX * 2;
- return gfx::Rect(client_bounds.x(),
- std::max(0, client_bounds.y() - kCaptionHeight),
- std::max(header_width, client_bounds.width()),
- client_bounds.height() + kCaptionHeight);
-}
-
-int ShellWindowFrameView::NonClientHitTest(const gfx::Point& point) {
- if (frame_->IsFullscreen())
- return HTCLIENT;
-
- int resize_inside_bounds_size = kResizeInsideBoundsSize;
- int resize_area_corner_size = kResizeAreaCornerSize;
-
-#if defined(USE_ASH)
- gfx::Rect expanded_bounds = bounds();
- int outside_bounds = ash::kResizeOutsideBoundsSize;
- if (aura::Env::GetInstance()->is_touch_down())
- outside_bounds *= ash::kResizeOutsideBoundsScaleForTouch;
- expanded_bounds.Inset(-outside_bounds, -outside_bounds);
- if (!expanded_bounds.Contains(point))
- return HTNOWHERE;
-
- resize_inside_bounds_size = ash::kResizeInsideBoundsSize;
- resize_area_corner_size = ash::kResizeAreaCornerSize;
-#endif
-
- // Check the frame first, as we allow a small area overlapping the contents
- // to be used for resize handles.
- bool can_ever_resize = frame_->widget_delegate() ?
- frame_->widget_delegate()->CanResize() :
- false;
- if (can_ever_resize) {
- // Don't allow overlapping resize handles when the window is maximized or
- // fullscreen, as it can't be resized in those states.
- int resize_border =
- frame_->IsMaximized() || frame_->IsFullscreen() ? 0 :
- resize_inside_bounds_size;
- int frame_component = GetHTComponentForFrame(point,
- resize_border,
- resize_border,
- resize_area_corner_size,
- resize_area_corner_size,
- can_ever_resize);
- if (frame_component != HTNOWHERE)
- return frame_component;
- }
-
- // Check for possible draggable region in the client area for the frameless
- // window.
- if (window_->frameless() &&
- window_->draggable_region() &&
- window_->draggable_region()->contains(point.x(), point.y()))
- return HTCAPTION;
-
- int client_component = frame_->client_view()->NonClientHitTest(point);
- if (client_component != HTNOWHERE)
- return client_component;
-
- // Then see if the point is within any of the window controls.
- if (close_button_ && close_button_->visible() &&
- close_button_->GetMirroredBounds().Contains(point))
- return HTCLOSE;
-
- // Caption is a safe default.
- return HTCAPTION;
-}
-
-void ShellWindowFrameView::GetWindowMask(const gfx::Size& size,
- gfx::Path* window_mask) {
- // We got nothing to say about no window mask.
-}
-
-gfx::Size ShellWindowFrameView::GetPreferredSize() {
- gfx::Size pref = frame_->client_view()->GetPreferredSize();
- gfx::Rect bounds(0, 0, pref.width(), pref.height());
- return frame_->non_client_view()->GetWindowBoundsForClientBounds(
- bounds).size();
-}
+NativeAppWindowViews::NativeAppWindowViews(
+ ShellWindow* shell_window,
+ const ShellWindow::CreateParams& create_params)
+ : shell_window_(shell_window),
+ web_view_(NULL),
+ window_(NULL),
+ is_fullscreen_(false),
+ frameless_(create_params.frame == ShellWindow::FRAME_NONE) {
+ minimum_size_ = create_params.minimum_size;
+ maximum_size_ = create_params.maximum_size;
-void ShellWindowFrameView::Layout() {
- if (window_->frameless())
- return;
- gfx::Size close_size = close_button_->GetPreferredSize();
- int closeButtonOffsetY =
- (kCaptionHeight - close_size.height()) / 2;
- const int kButtonSpacing = 9;
-
- close_button_->SetBounds(
- width() - kButtonSpacing - close_size.width(),
- closeButtonOffsetY,
- close_size.width(),
- close_size.height());
-
- bool can_ever_resize = frame_->widget_delegate() ?
- frame_->widget_delegate()->CanResize() :
- false;
- maximize_button_->SetEnabled(can_ever_resize);
- gfx::Size maximize_size = maximize_button_->GetPreferredSize();
- maximize_button_->SetBounds(
- close_button_->x() - kButtonSpacing - maximize_size.width(),
- closeButtonOffsetY,
- maximize_size.width(),
- maximize_size.height());
- gfx::Size restore_size = restore_button_->GetPreferredSize();
- restore_button_->SetBounds(
- close_button_->x() - kButtonSpacing - restore_size.width(),
- closeButtonOffsetY,
- restore_size.width(),
- restore_size.height());
-
- bool maximized = frame_->IsMaximized();
- maximize_button_->SetVisible(!maximized);
- restore_button_->SetVisible(maximized);
- if (maximized)
- maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
+ window_ = new views::Widget;
+ if (create_params.window_type == ShellWindow::WINDOW_TYPE_PANEL)
+ InitializePanelWindow(create_params);
else
- restore_button_->SetState(views::CustomButton::STATE_NORMAL);
+ InitializeDefaultWindow(create_params);
- gfx::Size minimize_size = minimize_button_->GetPreferredSize();
- minimize_button_->SetBounds(
- maximize_button_->x() - kButtonSpacing - minimize_size.width(),
- closeButtonOffsetY,
- minimize_size.width(),
- minimize_size.height());
-}
+ extension_keybinding_registry_.reset(
+ new ExtensionKeybindingRegistryViews(
+ profile(),
+ window_->GetFocusManager(),
+ extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
+ shell_window_));
-void ShellWindowFrameView::OnPaint(gfx::Canvas* canvas) {
- if (window_->frameless())
- return;
- // TODO(jeremya): different look for inactive?
- SkPaint paint;
- paint.setAntiAlias(false);
- paint.setStyle(SkPaint::kFill_Style);
- paint.setColor(SK_ColorWHITE);
- gfx::Path path;
- const int radius = frame_->IsMaximized() ? 0 : 1;
- path.moveTo(0, radius);
- path.lineTo(radius, 0);
- path.lineTo(width() - radius - 1, 0);
- path.lineTo(width(), radius + 1);
- path.lineTo(width(), kCaptionHeight);
- path.lineTo(0, kCaptionHeight);
- path.close();
- canvas->DrawPath(path, paint);
-}
-
-std::string ShellWindowFrameView::GetClassName() const {
- return kViewClassName;
-}
-
-gfx::Size ShellWindowFrameView::GetMinimumSize() {
- gfx::Size min_size = frame_->client_view()->GetMinimumSize();
- if (window_->frameless())
- return min_size;
-
- // Ensure we can display the top of the caption area.
- gfx::Rect client_bounds = GetBoundsForClientView();
- min_size.Enlarge(0, client_bounds.y());
- // Ensure we have enough space for the window icon and buttons. We allow
- // the title string to collapse to zero width.
- int closeButtonOffsetX =
- (kCaptionHeight - close_button_->height()) / 2;
- int header_width = close_button_->width() + closeButtonOffsetX * 2;
- if (header_width > min_size.width())
- min_size.set_width(header_width);
- return min_size;
-}
-
-gfx::Size ShellWindowFrameView::GetMaximumSize() {
- gfx::Size max_size = frame_->client_view()->GetMaximumSize();
- if (window_->frameless())
- return max_size;
-
- if (!max_size.IsEmpty()) {
- gfx::Rect client_bounds = GetBoundsForClientView();
- max_size.Enlarge(0, client_bounds.y());
- }
- return max_size;
+ OnViewWasResized();
+ window_->AddObserver(this);
}
-void ShellWindowFrameView::ButtonPressed(views::Button* sender,
- const ui::Event& event) {
- DCHECK(!window_->frameless());
- if (sender == close_button_)
- frame_->Close();
- else if (sender == maximize_button_)
- frame_->Maximize();
- else if (sender == restore_button_)
- frame_->Restore();
- else if (sender == minimize_button_)
- frame_->Minimize();
+NativeAppWindowViews::~NativeAppWindowViews() {
+ web_view_->SetWebContents(NULL);
}
-NativeAppWindowViews::NativeAppWindowViews(
- ShellWindow* shell_window,
- const ShellWindow::CreateParams& win_params)
- : shell_window_(shell_window),
- web_view_(NULL),
- is_fullscreen_(false),
- frameless_(win_params.frame == ShellWindow::CreateParams::FRAME_NONE) {
- window_ = new views::Widget;
- views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
- params.delegate = this;
- params.remove_standard_frame = true;
- params.use_system_default_icon = true;
- minimum_size_ = win_params.minimum_size;
- maximum_size_ = win_params.maximum_size;
- window_->Init(params);
+void NativeAppWindowViews::InitializeDefaultWindow(
+ const ShellWindow::CreateParams& create_params) {
+ views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW);
+ init_params.delegate = this;
+ init_params.remove_standard_frame = true;
+ init_params.use_system_default_icon = true;
+ window_->Init(init_params);
gfx::Rect window_bounds =
window_->non_client_view()->GetWindowBoundsForClientBounds(
- win_params.bounds);
+ create_params.bounds);
// Center window if no position was specified.
- if (win_params.bounds.x() == INT_MIN || win_params.bounds.y() == INT_MIN) {
+ if (create_params.bounds.x() == INT_MIN ||
+ create_params.bounds.y() == INT_MIN) {
window_->CenterWindow(window_bounds.size());
} else {
window_->SetBounds(window_bounds);
}
+
#if defined(OS_WIN) && !defined(USE_AURA)
std::string app_name = web_app::GenerateApplicationNameFromExtensionId(
extension()->id());
@@ -427,71 +97,35 @@ NativeAppWindowViews::NativeAppWindowViews(
UTF8ToWide(app_name), shell_window_->profile()->GetPath()),
GetWidget()->GetTopLevelWidget()->GetNativeWindow());
#endif
-
- extension_keybinding_registry_.reset(
- new ExtensionKeybindingRegistryViews(shell_window_->profile(),
- window_->GetFocusManager(),
- extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
- shell_window_));
-
- OnViewWasResized();
- window_->AddObserver(this);
-}
-
-views::View* NativeAppWindowViews::GetInitiallyFocusedView() {
- return web_view_;
-}
-
-bool NativeAppWindowViews::ShouldDescendIntoChildForEventHandling(
- gfx::NativeView child,
- const gfx::Point& location) {
-#if defined(USE_AURA)
- DCHECK_EQ(child, web_view_->web_contents()->GetView()->GetNativeView());
- // Shell window should claim mouse events that fall within the draggable
- // region.
- return !draggable_region_.get() ||
- !draggable_region_->contains(location.x(), location.y());
-#else
- return true;
-#endif
-}
-
-void NativeAppWindowViews::OnFocus() {
- web_view_->RequestFocus();
}
-void NativeAppWindowViews::ViewHierarchyChanged(
- bool is_add, views::View *parent, views::View *child) {
- if (is_add && child == this) {
- web_view_ = new views::WebView(NULL);
- AddChildView(web_view_);
- web_view_->SetWebContents(web_contents());
- }
-}
+void NativeAppWindowViews::InitializePanelWindow(
+ const ShellWindow::CreateParams& create_params) {
+ views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL);
+ params.delegate = this;
-gfx::Size NativeAppWindowViews::GetMinimumSize() {
- return minimum_size_;
-}
+ preferred_size_ = gfx::Size(create_params.bounds.width(),
+ create_params.bounds.height());
+ if (preferred_size_.width() == 0)
+ preferred_size_.set_width(kDefaultPanelWidth);
+ else if (preferred_size_.width() < kMinPanelWidth)
+ preferred_size_.set_width(kMinPanelWidth);
-gfx::Size NativeAppWindowViews::GetMaximumSize() {
- return maximum_size_;
-}
+ if (preferred_size_.height() == 0)
+ preferred_size_.set_height(kDefaultPanelHeight);
+ else if (preferred_size_.height() < kMinPanelHeight)
+ preferred_size_.set_height(kMinPanelHeight);
-void NativeAppWindowViews::SetFullscreen(bool fullscreen) {
- is_fullscreen_ = fullscreen;
- window_->SetFullscreen(fullscreen);
- // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we
- // ever drop the window out of fullscreen in response to something that
- // wasn't the app calling webkitCancelFullScreen().
-}
+ params.bounds = gfx::Rect(preferred_size_.width(), preferred_size_.height());
+ window_->Init(params);
-bool NativeAppWindowViews::IsFullscreenOrPending() const {
- return is_fullscreen_;
+ gfx::Rect window_bounds =
+ window_->non_client_view()->GetWindowBoundsForClientBounds(
+ create_params.bounds);
+ window_->SetBounds(window_bounds);
}
-NativeAppWindowViews::~NativeAppWindowViews() {
- web_view_->SetWebContents(NULL);
-}
+// BaseWindow implementation.
bool NativeAppWindowViews::IsActive() const {
return window_->IsActive();
@@ -573,52 +207,11 @@ void NativeAppWindowViews::FlashFrame(bool flash) {
}
bool NativeAppWindowViews::IsAlwaysOnTop() const {
- return false;
-}
-
-void NativeAppWindowViews::DeleteDelegate() {
- window_->RemoveObserver(this);
- shell_window_->OnNativeClose();
-}
-
-bool NativeAppWindowViews::CanResize() const {
- return maximum_size_.IsEmpty() || minimum_size_ != maximum_size_;
-}
-
-bool NativeAppWindowViews::CanMaximize() const {
- return maximum_size_.IsEmpty();
-}
-
-views::View* NativeAppWindowViews::GetContentsView() {
- return this;
-}
-
-views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView(
- views::Widget* widget) {
-#if defined(USE_ASH)
- if (chrome::IsNativeViewInAsh(widget->GetNativeView()) && !frameless_) {
- ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh();
- frame->Init(widget);
- return frame;
- }
-#endif
- ShellWindowFrameView* frame_view = new ShellWindowFrameView(this);
- frame_view->Init(window_);
- return frame_view;
-}
-
-string16 NativeAppWindowViews::GetWindowTitle() const {
- return shell_window_->GetTitle();
-}
-
-views::Widget* NativeAppWindowViews::GetWidget() {
- return window_;
-}
-
-const views::Widget* NativeAppWindowViews::GetWidget() const {
- return window_;
+ return shell_window_->window_type() == ShellWindow::WINDOW_TYPE_PANEL;
}
+// Private method. TODO(stevenjb): Move this below InitializePanelWindow()
+// to match declaration order.
void NativeAppWindowViews::OnViewWasResized() {
// TODO(jeremya): this doesn't seem like a terribly elegant way to keep the
// window shape in sync.
@@ -671,6 +264,32 @@ void NativeAppWindowViews::OnViewWasResized() {
#endif
}
+// WidgetDelegate implementation.
+
+void NativeAppWindowViews::OnWidgetMove() {
+ shell_window_->OnNativeWindowChanged();
+}
+
+views::View* NativeAppWindowViews::GetInitiallyFocusedView() {
+ return web_view_;
+}
+
+bool NativeAppWindowViews::CanResize() const {
+ return maximum_size_.IsEmpty() || minimum_size_ != maximum_size_;
+}
+
+bool NativeAppWindowViews::CanMaximize() const {
+ return maximum_size_.IsEmpty();
+}
+
+string16 NativeAppWindowViews::GetWindowTitle() const {
+ return shell_window_->GetTitle();
+}
+
+bool NativeAppWindowViews::ShouldShowWindowTitle() const {
+ return false;
+}
+
gfx::ImageSkia NativeAppWindowViews::GetWindowAppIcon() {
gfx::Image app_icon = shell_window_->app_icon();
if (app_icon.IsEmpty())
@@ -691,14 +310,62 @@ gfx::ImageSkia NativeAppWindowViews::GetWindowIcon() {
return gfx::ImageSkia();
}
-bool NativeAppWindowViews::ShouldShowWindowTitle() const {
- return false;
+void NativeAppWindowViews::SaveWindowPlacement(const gfx::Rect& bounds,
+ ui::WindowShowState show_state) {
+ views::WidgetDelegate::SaveWindowPlacement(bounds, show_state);
+ shell_window_->OnNativeWindowChanged();
}
-void NativeAppWindowViews::OnWidgetMove() {
- shell_window_->OnNativeWindowChanged();
+void NativeAppWindowViews::DeleteDelegate() {
+ window_->RemoveObserver(this);
+ shell_window_->OnNativeClose();
}
+views::Widget* NativeAppWindowViews::GetWidget() {
+ return window_;
+}
+
+const views::Widget* NativeAppWindowViews::GetWidget() const {
+ return window_;
+}
+
+views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView(
+ views::Widget* widget) {
+#if defined(USE_ASH)
+ if (chrome::IsNativeViewInAsh(widget->GetNativeView())) {
+ if (shell_window_->window_type() == ShellWindow::WINDOW_TYPE_PANEL) {
+ ash::PanelFrameView::FrameType frame_type = frameless_ ?
+ ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH;
+ return new ash::PanelFrameView(widget, frame_type);
+ }
+ if (!frameless_) {
+ ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh();
+ frame->Init(widget);
+ return frame;
+ }
+ }
+#endif
+ ShellWindowFrameView* frame_view = new ShellWindowFrameView(this);
+ frame_view->Init(window_);
+ return frame_view;
+}
+
+bool NativeAppWindowViews::ShouldDescendIntoChildForEventHandling(
+ gfx::NativeView child,
+ const gfx::Point& location) {
+#if defined(USE_AURA)
+ DCHECK_EQ(child, web_view_->web_contents()->GetView()->GetNativeView());
+ // Shell window should claim mouse events that fall within the draggable
+ // region.
+ return !draggable_region_.get() ||
+ !draggable_region_->contains(location.x(), location.y());
+#else
+ return true;
+#endif
+}
+
+// WidgetObserver implementation.
+
void NativeAppWindowViews::OnWidgetVisibilityChanged(views::Widget* widget,
bool visible) {
shell_window_->OnNativeWindowChanged();
@@ -709,12 +376,62 @@ void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget* widget,
shell_window_->OnNativeWindowChanged();
}
+// views::View implementation.
+
void NativeAppWindowViews::Layout() {
DCHECK(web_view_);
web_view_->SetBounds(0, 0, width(), height());
OnViewWasResized();
}
+void NativeAppWindowViews::ViewHierarchyChanged(
+ bool is_add, views::View *parent, views::View *child) {
+ if (is_add && child == this) {
+ web_view_ = new views::WebView(NULL);
+ AddChildView(web_view_);
+ web_view_->SetWebContents(web_contents());
+ }
+}
+
+gfx::Size NativeAppWindowViews::GetPreferredSize() {
+ if (!preferred_size_.IsEmpty())
+ return preferred_size_;
+ return views::View::GetPreferredSize();
+}
+
+gfx::Size NativeAppWindowViews::GetMinimumSize() {
+ return minimum_size_;
+}
+
+gfx::Size NativeAppWindowViews::GetMaximumSize() {
+ return maximum_size_;
+}
+
+void NativeAppWindowViews::OnFocus() {
+ web_view_->RequestFocus();
+}
+
+// NativeAppWindow implementation.
+
+void NativeAppWindowViews::SetFullscreen(bool fullscreen) {
+ // Fullscreen not supported by panels.
+ if (shell_window_->window_type() == ShellWindow::WINDOW_TYPE_PANEL)
+ return;
+ is_fullscreen_ = fullscreen;
+ window_->SetFullscreen(fullscreen);
+ // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we
+ // ever drop the window out of fullscreen in response to something that
+ // wasn't the app calling webkitCancelFullScreen().
+}
+
+bool NativeAppWindowViews::IsFullscreenOrPending() const {
+ return is_fullscreen_;
+}
+
+views::View* NativeAppWindowViews::GetContentsView() {
+ return this;
+}
+
void NativeAppWindowViews::UpdateWindowIcon() {
window_->UpdateWindowIcon();
}
@@ -743,11 +460,8 @@ void NativeAppWindowViews::RenderViewHostChanged() {
OnViewWasResized();
}
-void NativeAppWindowViews::SaveWindowPlacement(const gfx::Rect& bounds,
- ui::WindowShowState show_state) {
- views::WidgetDelegate::SaveWindowPlacement(bounds, show_state);
- shell_window_->OnNativeWindowChanged();
-}
+//------------------------------------------------------------------------------
+// NativeAppWindow::Create
// static
NativeAppWindow* NativeAppWindow::Create(
diff --git a/chrome/browser/ui/views/extensions/native_app_window_views.h b/chrome/browser/ui/views/extensions/native_app_window_views.h
index ac6d30c..4c2ef2a 100644
--- a/chrome/browser/ui/views/extensions/native_app_window_views.h
+++ b/chrome/browser/ui/views/extensions/native_app_window_views.h
@@ -36,10 +36,16 @@ class NativeAppWindowViews : public NativeAppWindow,
public:
NativeAppWindowViews(ShellWindow* shell_window,
const ShellWindow::CreateParams& params);
+ virtual ~NativeAppWindowViews();
bool frameless() const { return frameless_; }
SkRegion* draggable_region() { return draggable_region_.get(); }
+ private:
+ void InitializeDefaultWindow(const ShellWindow::CreateParams& create_params);
+ void InitializePanelWindow(const ShellWindow::CreateParams& create_params);
+ void OnViewWasResized();
+
// BaseWindow implementation.
virtual bool IsActive() const OVERRIDE;
virtual bool IsMaximized() const OVERRIDE;
@@ -62,23 +68,25 @@ class NativeAppWindowViews : public NativeAppWindow,
virtual bool IsAlwaysOnTop() const OVERRIDE;
// WidgetDelegate implementation.
- virtual views::View* GetContentsView() OVERRIDE;
- virtual views::NonClientFrameView* CreateNonClientFrameView(
- views::Widget* widget) OVERRIDE;
+ virtual void OnWidgetMove() OVERRIDE;
+ virtual views::View* GetInitiallyFocusedView() OVERRIDE;
virtual bool CanResize() const OVERRIDE;
virtual bool CanMaximize() const OVERRIDE;
- virtual views::Widget* GetWidget() OVERRIDE;
- virtual const views::Widget* GetWidget() const OVERRIDE;
virtual string16 GetWindowTitle() const OVERRIDE;
+ virtual bool ShouldShowWindowTitle() const OVERRIDE;
+ virtual gfx::ImageSkia GetWindowAppIcon() OVERRIDE;
+ virtual gfx::ImageSkia GetWindowIcon() OVERRIDE;
+ virtual void SaveWindowPlacement(const gfx::Rect& bounds,
+ ui::WindowShowState show_state) OVERRIDE;
virtual void DeleteDelegate() OVERRIDE;
- virtual views::View* GetInitiallyFocusedView() OVERRIDE;
+ virtual views::Widget* GetWidget() OVERRIDE;
+ virtual const views::Widget* GetWidget() const OVERRIDE;
+ virtual views::View* GetContentsView() OVERRIDE;
+ virtual views::NonClientFrameView* CreateNonClientFrameView(
+ views::Widget* widget) OVERRIDE;
virtual bool ShouldDescendIntoChildForEventHandling(
gfx::NativeView child,
const gfx::Point& location) OVERRIDE;
- virtual gfx::ImageSkia GetWindowAppIcon() OVERRIDE;
- virtual gfx::ImageSkia GetWindowIcon() OVERRIDE;
- virtual bool ShouldShowWindowTitle() const OVERRIDE;
- virtual void OnWidgetMove() OVERRIDE;
// WidgetObserver implementation.
virtual void OnWidgetVisibilityChanged(views::Widget* widget,
@@ -86,32 +94,15 @@ class NativeAppWindowViews : public NativeAppWindow,
virtual void OnWidgetActivationChanged(views::Widget* widget,
bool active) OVERRIDE;
- protected:
// views::View implementation.
virtual void Layout() OVERRIDE;
virtual void ViewHierarchyChanged(
bool is_add, views::View *parent, views::View *child) OVERRIDE;
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
virtual gfx::Size GetMinimumSize() OVERRIDE;
virtual gfx::Size GetMaximumSize() OVERRIDE;
virtual void OnFocus() OVERRIDE;
- Profile* profile() { return shell_window_->profile(); }
- content::WebContents* web_contents() {
- return shell_window_->web_contents();
- }
- const extensions::Extension* extension() {
- return shell_window_->extension();
- }
-
- // views::WidgetDelegate implementation.
- virtual void SaveWindowPlacement(const gfx::Rect& bounds,
- ui::WindowShowState show_state) OVERRIDE;
-
- private:
- friend class ShellWindowFrameView;
-
- virtual ~NativeAppWindowViews();
-
// NativeAppWindow implementation.
virtual void SetFullscreen(bool fullscreen) OVERRIDE;
virtual bool IsFullscreenOrPending() const OVERRIDE;
@@ -123,10 +114,15 @@ class NativeAppWindowViews : public NativeAppWindow,
const content::NativeWebKeyboardEvent& event) OVERRIDE;
virtual void RenderViewHostChanged() OVERRIDE;
- void OnViewWasResized();
+ Profile* profile() { return shell_window_->profile(); }
+ content::WebContents* web_contents() {
+ return shell_window_->web_contents();
+ }
+ const extensions::Extension* extension() {
+ return shell_window_->extension();
+ }
ShellWindow* shell_window_; // weak - ShellWindow owns NativeAppWindow.
-
views::WebView* web_view_;
views::Widget* window_;
bool is_fullscreen_;
@@ -136,6 +132,7 @@ class NativeAppWindowViews : public NativeAppWindow,
bool frameless_;
gfx::Size minimum_size_;
gfx::Size maximum_size_;
+ gfx::Size preferred_size_;
// The class that registers for keyboard shortcuts for extension commands.
scoped_ptr<ExtensionKeybindingRegistryViews> extension_keybinding_registry_;
diff --git a/chrome/browser/ui/views/extensions/shell_window_frame_view.cc b/chrome/browser/ui/views/extensions/shell_window_frame_view.cc
new file mode 100644
index 0000000..3d8b761
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/shell_window_frame_view.cc
@@ -0,0 +1,348 @@
+// 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 "chrome/browser/ui/views/extensions/shell_window_frame_view.h"
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/ui/views/extensions/native_app_window_views.h"
+#include "chrome/common/extensions/draggable_region.h"
+#include "grit/theme_resources.h"
+#include "grit/ui_strings.h" // Accessibility names
+#include "third_party/skia/include/core/SkPaint.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/path.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/views_delegate.h"
+#include "ui/views/widget/widget.h"
+
+#if defined(OS_WIN) && !defined(USE_AURA)
+#include "chrome/browser/shell_integration.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "ui/base/win/shell.h"
+#endif
+
+#if defined(USE_ASH)
+#include "ash/ash_constants.h"
+#include "chrome/browser/ui/ash/ash_util.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#endif
+
+namespace {
+const int kResizeInsideBoundsSize = 5;
+const int kResizeAreaCornerSize = 16;
+
+// Height of the chrome-style caption, in pixels.
+const int kCaptionHeight = 25;
+} // namespace
+
+
+const char ShellWindowFrameView::kViewClassName[] =
+ "browser/ui/views/extensions/ShellWindowFrameView";
+
+ShellWindowFrameView::ShellWindowFrameView(NativeAppWindowViews* window)
+ : window_(window),
+ frame_(NULL),
+ close_button_(NULL) {
+}
+
+ShellWindowFrameView::~ShellWindowFrameView() {
+}
+
+void ShellWindowFrameView::Init(views::Widget* frame) {
+ frame_ = frame;
+
+ if (!window_->frameless()) {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ close_button_ = new views::ImageButton(this);
+ close_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE).ToImageSkia());
+ close_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_H).ToImageSkia());
+ close_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_P).ToImageSkia());
+ close_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
+ AddChildView(close_button_);
+ maximize_button_ = new views::ImageButton(this);
+ maximize_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE).ToImageSkia());
+ maximize_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_H).ToImageSkia());
+ maximize_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_P).ToImageSkia());
+ maximize_button_->SetImage(views::CustomButton::STATE_DISABLED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_D).ToImageSkia());
+ maximize_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
+ AddChildView(maximize_button_);
+ restore_button_ = new views::ImageButton(this);
+ restore_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE).ToImageSkia());
+ restore_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_H).ToImageSkia());
+ restore_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_P).ToImageSkia());
+ restore_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_RESTORE));
+ AddChildView(restore_button_);
+ minimize_button_ = new views::ImageButton(this);
+ minimize_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE).ToImageSkia());
+ minimize_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_H).ToImageSkia());
+ minimize_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_P).ToImageSkia());
+ minimize_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
+ AddChildView(minimize_button_);
+ }
+
+#if defined(USE_ASH)
+ aura::Window* window = frame->GetNativeWindow();
+ if (chrome::IsNativeWindowInAsh(window)) {
+ // Ensure we get resize cursors for a few pixels outside our bounds.
+ window->SetHitTestBoundsOverrideOuter(
+ gfx::Insets(-ash::kResizeOutsideBoundsSize,
+ -ash::kResizeOutsideBoundsSize,
+ -ash::kResizeOutsideBoundsSize,
+ -ash::kResizeOutsideBoundsSize),
+ ash::kResizeOutsideBoundsScaleForTouch);
+ // Ensure we get resize cursors just inside our bounds as well.
+ // TODO(jeremya): do we need to update these when in fullscreen/maximized?
+ window->set_hit_test_bounds_override_inner(
+ gfx::Insets(ash::kResizeInsideBoundsSize, ash::kResizeInsideBoundsSize,
+ ash::kResizeInsideBoundsSize,
+ ash::kResizeInsideBoundsSize));
+ }
+#endif
+}
+
+// views::NonClientFrameView implementation.
+
+gfx::Rect ShellWindowFrameView::GetBoundsForClientView() const {
+ if (window_->frameless() || frame_->IsFullscreen())
+ return bounds();
+ return gfx::Rect(0, kCaptionHeight, width(),
+ std::max(0, height() - kCaptionHeight));
+}
+
+gfx::Rect ShellWindowFrameView::GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const {
+ if (window_->frameless()) {
+ gfx::Rect window_bounds = client_bounds;
+ // Enforce minimum size (1, 1) in case that client_bounds is passed with
+ // empty size. This could occur when the frameless window is being
+ // initialized.
+ if (window_bounds.IsEmpty()) {
+ window_bounds.set_width(1);
+ window_bounds.set_height(1);
+ }
+ return window_bounds;
+ }
+
+ int closeButtonOffsetX =
+ (kCaptionHeight - close_button_->height()) / 2;
+ int header_width = close_button_->width() + closeButtonOffsetX * 2;
+ return gfx::Rect(client_bounds.x(),
+ std::max(0, client_bounds.y() - kCaptionHeight),
+ std::max(header_width, client_bounds.width()),
+ client_bounds.height() + kCaptionHeight);
+}
+
+int ShellWindowFrameView::NonClientHitTest(const gfx::Point& point) {
+ if (frame_->IsFullscreen())
+ return HTCLIENT;
+
+ int resize_inside_bounds_size = kResizeInsideBoundsSize;
+ int resize_area_corner_size = kResizeAreaCornerSize;
+
+#if defined(USE_ASH)
+ gfx::Rect expanded_bounds = bounds();
+ int outside_bounds = ash::kResizeOutsideBoundsSize;
+ if (aura::Env::GetInstance()->is_touch_down())
+ outside_bounds *= ash::kResizeOutsideBoundsScaleForTouch;
+ expanded_bounds.Inset(-outside_bounds, -outside_bounds);
+ if (!expanded_bounds.Contains(point))
+ return HTNOWHERE;
+
+ resize_inside_bounds_size = ash::kResizeInsideBoundsSize;
+ resize_area_corner_size = ash::kResizeAreaCornerSize;
+#endif
+
+ // Check the frame first, as we allow a small area overlapping the contents
+ // to be used for resize handles.
+ bool can_ever_resize = frame_->widget_delegate() ?
+ frame_->widget_delegate()->CanResize() :
+ false;
+ if (can_ever_resize) {
+ // Don't allow overlapping resize handles when the window is maximized or
+ // fullscreen, as it can't be resized in those states.
+ int resize_border =
+ frame_->IsMaximized() || frame_->IsFullscreen() ? 0 :
+ resize_inside_bounds_size;
+ int frame_component = GetHTComponentForFrame(point,
+ resize_border,
+ resize_border,
+ resize_area_corner_size,
+ resize_area_corner_size,
+ can_ever_resize);
+ if (frame_component != HTNOWHERE)
+ return frame_component;
+ }
+
+ // Check for possible draggable region in the client area for the frameless
+ // window.
+ if (window_->frameless() &&
+ window_->draggable_region() &&
+ window_->draggable_region()->contains(point.x(), point.y()))
+ return HTCAPTION;
+
+ int client_component = frame_->client_view()->NonClientHitTest(point);
+ if (client_component != HTNOWHERE)
+ return client_component;
+
+ // Then see if the point is within any of the window controls.
+ if (close_button_ && close_button_->visible() &&
+ close_button_->GetMirroredBounds().Contains(point))
+ return HTCLOSE;
+
+ // Caption is a safe default.
+ return HTCAPTION;
+}
+
+void ShellWindowFrameView::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+ // We got nothing to say about no window mask.
+}
+
+// views::View implementation.
+
+gfx::Size ShellWindowFrameView::GetPreferredSize() {
+ gfx::Size pref = frame_->client_view()->GetPreferredSize();
+ gfx::Rect bounds(0, 0, pref.width(), pref.height());
+ return frame_->non_client_view()->GetWindowBoundsForClientBounds(
+ bounds).size();
+}
+
+void ShellWindowFrameView::Layout() {
+ if (window_->frameless())
+ return;
+ gfx::Size close_size = close_button_->GetPreferredSize();
+ int closeButtonOffsetY =
+ (kCaptionHeight - close_size.height()) / 2;
+ const int kButtonSpacing = 9;
+
+ close_button_->SetBounds(
+ width() - kButtonSpacing - close_size.width(),
+ closeButtonOffsetY,
+ close_size.width(),
+ close_size.height());
+
+ bool can_ever_resize = frame_->widget_delegate() ?
+ frame_->widget_delegate()->CanResize() :
+ false;
+ maximize_button_->SetEnabled(can_ever_resize);
+ gfx::Size maximize_size = maximize_button_->GetPreferredSize();
+ maximize_button_->SetBounds(
+ close_button_->x() - kButtonSpacing - maximize_size.width(),
+ closeButtonOffsetY,
+ maximize_size.width(),
+ maximize_size.height());
+ gfx::Size restore_size = restore_button_->GetPreferredSize();
+ restore_button_->SetBounds(
+ close_button_->x() - kButtonSpacing - restore_size.width(),
+ closeButtonOffsetY,
+ restore_size.width(),
+ restore_size.height());
+
+ bool maximized = frame_->IsMaximized();
+ maximize_button_->SetVisible(!maximized);
+ restore_button_->SetVisible(maximized);
+ if (maximized)
+ maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
+ else
+ restore_button_->SetState(views::CustomButton::STATE_NORMAL);
+
+ gfx::Size minimize_size = minimize_button_->GetPreferredSize();
+ minimize_button_->SetBounds(
+ maximize_button_->x() - kButtonSpacing - minimize_size.width(),
+ closeButtonOffsetY,
+ minimize_size.width(),
+ minimize_size.height());
+}
+
+void ShellWindowFrameView::OnPaint(gfx::Canvas* canvas) {
+ if (window_->frameless())
+ return;
+ // TODO(jeremya): different look for inactive?
+ SkPaint paint;
+ paint.setAntiAlias(false);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorWHITE);
+ gfx::Path path;
+ const int radius = frame_->IsMaximized() ? 0 : 1;
+ path.moveTo(0, radius);
+ path.lineTo(radius, 0);
+ path.lineTo(width() - radius - 1, 0);
+ path.lineTo(width(), radius + 1);
+ path.lineTo(width(), kCaptionHeight);
+ path.lineTo(0, kCaptionHeight);
+ path.close();
+ canvas->DrawPath(path, paint);
+}
+
+std::string ShellWindowFrameView::GetClassName() const {
+ return kViewClassName;
+}
+
+gfx::Size ShellWindowFrameView::GetMinimumSize() {
+ gfx::Size min_size = frame_->client_view()->GetMinimumSize();
+ if (window_->frameless())
+ return min_size;
+
+ // Ensure we can display the top of the caption area.
+ gfx::Rect client_bounds = GetBoundsForClientView();
+ min_size.Enlarge(0, client_bounds.y());
+ // Ensure we have enough space for the window icon and buttons. We allow
+ // the title string to collapse to zero width.
+ int closeButtonOffsetX =
+ (kCaptionHeight - close_button_->height()) / 2;
+ int header_width = close_button_->width() + closeButtonOffsetX * 2;
+ if (header_width > min_size.width())
+ min_size.set_width(header_width);
+ return min_size;
+}
+
+gfx::Size ShellWindowFrameView::GetMaximumSize() {
+ gfx::Size max_size = frame_->client_view()->GetMaximumSize();
+ if (window_->frameless())
+ return max_size;
+
+ if (!max_size.IsEmpty()) {
+ gfx::Rect client_bounds = GetBoundsForClientView();
+ max_size.Enlarge(0, client_bounds.y());
+ }
+ return max_size;
+}
+
+// views::ButtonListener implementation.
+
+void ShellWindowFrameView::ButtonPressed(views::Button* sender,
+ const ui::Event& event) {
+ DCHECK(!window_->frameless());
+ if (sender == close_button_)
+ frame_->Close();
+ else if (sender == maximize_button_)
+ frame_->Maximize();
+ else if (sender == restore_button_)
+ frame_->Restore();
+ else if (sender == minimize_button_)
+ frame_->Minimize();
+}
diff --git a/chrome/browser/ui/views/extensions/shell_window_frame_view.h b/chrome/browser/ui/views/extensions/shell_window_frame_view.h
new file mode 100644
index 0000000..c015482
--- /dev/null
+++ b/chrome/browser/ui/views/extensions/shell_window_frame_view.h
@@ -0,0 +1,77 @@
+// 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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_SHELL_WINDOW_FRAME_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_SHELL_WINDOW_FRAME_VIEW_H_
+
+#include <string>
+
+#include "ui/gfx/path.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/window/non_client_view.h"
+
+namespace gfx {
+class Canvas;
+class Point;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace views {
+class ImageButton;
+class Widget;
+}
+
+class NativeAppWindowViews;
+
+// A frameless or non-Ash, non-panel NonClientFrameView for app windows.
+class ShellWindowFrameView : public views::NonClientFrameView,
+ public views::ButtonListener {
+ public:
+ static const char kViewClassName[];
+
+ explicit ShellWindowFrameView(NativeAppWindowViews* window);
+ virtual ~ShellWindowFrameView();
+
+ void Init(views::Widget* frame);
+
+ private:
+ // views::NonClientFrameView implementation.
+ virtual gfx::Rect GetBoundsForClientView() const OVERRIDE;
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const OVERRIDE;
+ virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE;
+ virtual void GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) OVERRIDE;
+ virtual void ResetWindowControls() OVERRIDE {}
+ virtual void UpdateWindowIcon() OVERRIDE {}
+ virtual void UpdateWindowTitle() OVERRIDE {}
+
+ // views::View implementation.
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void Layout() OVERRIDE;
+ virtual std::string GetClassName() const OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual gfx::Size GetMinimumSize() OVERRIDE;
+ virtual gfx::Size GetMaximumSize() OVERRIDE;
+
+ // views::ButtonListener implementation.
+ virtual void ButtonPressed(views::Button* sender, const ui::Event& event)
+ OVERRIDE;
+
+ NativeAppWindowViews* window_;
+ views::Widget* frame_;
+ views::ImageButton* close_button_;
+ views::ImageButton* maximize_button_;
+ views::ImageButton* restore_button_;
+ views::ImageButton* minimize_button_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellWindowFrameView);
+};
+
+#endif // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_SHELL_WINDOW_FRAME_VIEW_H_
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index e36461b..f28a844 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1453,6 +1453,8 @@
'browser/ui/views/extensions/media_galleries_dialog_views.h',
'browser/ui/views/extensions/native_app_window_views.cc',
'browser/ui/views/extensions/native_app_window_views.h',
+ 'browser/ui/views/extensions/shell_window_frame_view.cc',
+ 'browser/ui/views/extensions/shell_window_frame_view.h',
'browser/ui/views/external_protocol_dialog.cc',
'browser/ui/views/external_protocol_dialog.h',
'browser/ui/views/external_tab_container_win.cc',
diff --git a/chrome/common/extensions/api/app_window.idl b/chrome/common/extensions/api/app_window.idl
index 3ca6fb9..7f88000 100644
--- a/chrome/common/extensions/api/app_window.idl
+++ b/chrome/common/extensions/api/app_window.idl
@@ -49,7 +49,9 @@ namespace app.window {
// Maximum height of the window.
long? maxHeight;
- // Window type: 'shell' (the default) is the only currently supported value.
+ // Window type:
+ // 'shell' - the default window type
+ // 'panel' - a panel, managed by the OS (Currently experimental, Ash only)
[nodoc] DOMString? type;
// Frame type: 'none' or 'chrome' (defaults to 'chrome').