diff options
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'). |