diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-13 11:59:47 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-13 11:59:47 +0000 |
commit | ad678cf424e5c9c0d956deb94d0777d475e8fdd2 (patch) | |
tree | a6a55eb374813d142fe1429e824e2c8e16e38ac7 /ui | |
parent | fc29f183706373091bf980160e3d26835b264f9c (diff) | |
download | chromium_src-ad678cf424e5c9c0d956deb94d0777d475e8fdd2.zip chromium_src-ad678cf424e5c9c0d956deb94d0777d475e8fdd2.tar.gz chromium_src-ad678cf424e5c9c0d956deb94d0777d475e8fdd2.tar.bz2 |
views: Move views/window/ to ui/views/window directory.
Left stub files that will be removed in a follow up patch after updating
the files to point to the new location.
BUG=104039
R=ben@chromium.org
Review URL: http://codereview.chromium.org/8552005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@109827 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/views/window/client_view.cc | 88 | ||||
-rw-r--r-- | ui/views/window/client_view.h | 89 | ||||
-rw-r--r-- | ui/views/window/custom_frame_view.cc | 594 | ||||
-rw-r--r-- | ui/views/window/custom_frame_view.h | 127 | ||||
-rw-r--r-- | ui/views/window/dialog_client_view.cc | 591 | ||||
-rw-r--r-- | ui/views/window/dialog_client_view.h | 170 | ||||
-rw-r--r-- | ui/views/window/dialog_delegate.cc | 121 | ||||
-rw-r--r-- | ui/views/window/dialog_delegate.h | 128 | ||||
-rw-r--r-- | ui/views/window/native_frame_view.cc | 74 | ||||
-rw-r--r-- | ui/views/window/native_frame_view.h | 48 | ||||
-rw-r--r-- | ui/views/window/non_client_view.cc | 276 | ||||
-rw-r--r-- | ui/views/window/non_client_view.h | 240 | ||||
-rw-r--r-- | ui/views/window/window_resources.h | 32 | ||||
-rw-r--r-- | ui/views/window/window_shape.cc | 46 | ||||
-rw-r--r-- | ui/views/window/window_shape.h | 25 |
15 files changed, 2649 insertions, 0 deletions
diff --git a/ui/views/window/client_view.cc b/ui/views/window/client_view.cc new file mode 100644 index 0000000..560983b --- /dev/null +++ b/ui/views/window/client_view.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2011 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 "views/window/client_view.h" + +#include "base/logging.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/hit_test.h" +#include "views/widget/widget.h" +#include "views/widget/widget_delegate.h" + +namespace views { + +// static +const char ClientView::kViewClassName[] = + "views/window/ClientView"; + +/////////////////////////////////////////////////////////////////////////////// +// ClientView, public: + +ClientView::ClientView(Widget* widget, View* contents_view) + : widget_(widget), + contents_view_(contents_view) { +} + +int ClientView::NonClientHitTest(const gfx::Point& point) { + return bounds().Contains(point) ? HTCLIENT : HTNOWHERE; +} + +DialogClientView* ClientView::AsDialogClientView() { + return NULL; +} + +const DialogClientView* ClientView::AsDialogClientView() const { + return NULL; +} + +bool ClientView::CanClose() { + return true; +} + +void ClientView::WidgetClosing() { +} + +/////////////////////////////////////////////////////////////////////////////// +// ClientView, View overrides: + +gfx::Size ClientView::GetPreferredSize() { + // |contents_view_| is allowed to be NULL up until the point where this view + // is attached to a Container. + if (contents_view_) + return contents_view_->GetPreferredSize(); + return gfx::Size(); +} + +void ClientView::Layout() { + // |contents_view_| is allowed to be NULL up until the point where this view + // is attached to a Container. + if (contents_view_) + contents_view_->SetBounds(0, 0, width(), height()); +} + +std::string ClientView::GetClassName() const { + return kViewClassName; +} + +void ClientView::GetAccessibleState(ui::AccessibleViewState* state) { + state->role = ui::AccessibilityTypes::ROLE_CLIENT; +} + +void ClientView::OnBoundsChanged(const gfx::Rect& previous_bounds) { + // Overridden to do nothing. The NonClientView manually calls Layout on the + // ClientView when it is itself laid out, see comment in + // NonClientView::Layout. +} + +void ClientView::ViewHierarchyChanged(bool is_add, View* parent, View* child) { + if (is_add && child == this) { + DCHECK(GetWidget()); + DCHECK(contents_view_); // |contents_view_| must be valid now! + // Insert |contents_view_| at index 0 so it is first in the focus chain. + // (the OK/Cancel buttons are inserted before contents_view_) + AddChildViewAt(contents_view_, 0); + } +} + +} // namespace views diff --git a/ui/views/window/client_view.h b/ui/views/window/client_view.h new file mode 100644 index 0000000..4b809f2 --- /dev/null +++ b/ui/views/window/client_view.h @@ -0,0 +1,89 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_CLIENT_VIEW_H_ +#define UI_VIEWS_WINDOW_CLIENT_VIEW_H_ +#pragma once + +#include "views/view.h" + +namespace views { + +class DialogClientView; +class Widget; + +/////////////////////////////////////////////////////////////////////////////// +// ClientView +// +// A ClientView is a View subclass that is used to occupy the "client area" +// of a widget. It provides basic information to the widget that contains it +// such as non-client hit testing information, sizing etc. Sub-classes of +// ClientView are used to create more elaborate contents, e.g. +// "DialogClientView". +class VIEWS_EXPORT ClientView : public View { + public: + // Internal class name + static const char kViewClassName[]; + + // Constructs a ClientView object for the specified widget with the specified + // contents. Since this object is created during the process of creating + // |widget|, |contents_view| must be valid if you want the initial size of + // the widget to be based on |contents_view|'s preferred size. + ClientView(Widget* widget, View* contents_view); + virtual ~ClientView() {} + + // Manual RTTI ftw. + virtual DialogClientView* AsDialogClientView(); + virtual const DialogClientView* AsDialogClientView() const; + + // Returns true to signal that the Widget can be closed. Specialized + // ClientView subclasses can override this default behavior to allow the + // close to be blocked until the user corrects mistakes, accepts a warning + // dialog, etc. + virtual bool CanClose(); + + // Notification that the widget is closing. + virtual void WidgetClosing(); + + // Tests to see if the specified point (in view coordinates) is within the + // bounds of this view. If so, it returns HTCLIENT in this default + // implementation. If it is outside the bounds of this view, this must return + // HTNOWHERE to tell the caller to do further processing to determine where + // in the non-client area it is (if it is). + // Subclasses of ClientView can extend this logic by overriding this method + // to detect if regions within the client area count as parts of the "non- + // client" area. A good example of this is the size box at the bottom right + // corner of resizable dialog boxes. + virtual int NonClientHitTest(const gfx::Point& point); + + // Overridden from View: + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual void Layout() OVERRIDE; + virtual std::string GetClassName() const OVERRIDE; + + protected: + // Overridden from View: + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; + virtual void ViewHierarchyChanged(bool is_add, + View* parent, + View* child) OVERRIDE; + + // Accessors for private data members. + View* contents_view() const { return contents_view_; } + void set_contents_view(View* contents_view) { + contents_view_ = contents_view; + } + + private: + // The Widget that hosts this ClientView. + Widget* widget_; + + // The View that this ClientView contains. + View* contents_view_; +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_CLIENT_VIEW_H_ diff --git a/ui/views/window/custom_frame_view.cc b/ui/views/window/custom_frame_view.cc new file mode 100644 index 0000000..989f3c55 --- /dev/null +++ b/ui/views/window/custom_frame_view.cc @@ -0,0 +1,594 @@ +// Copyright (c) 2011 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 "views/window/custom_frame_view.h" + +#include <algorithm> + +#include "base/utf_string_conversions.h" +#include "grit/ui_resources.h" +#include "grit/ui_strings.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/font.h" +#include "ui/gfx/path.h" +#include "views/widget/widget_delegate.h" +#include "views/window/client_view.h" +#include "views/window/window_resources.h" +#include "views/window/window_shape.h" + +#if defined(USE_AURA) +#include "views/widget/native_widget_aura.h" +#elif defined(OS_WIN) +#include "views/widget/native_widget_win.h" +#endif + +namespace views { + +// static +gfx::Font* CustomFrameView::title_font_ = NULL; + +namespace { +// The frame border is only visible in restored mode and is hardcoded to 4 px on +// each side regardless of the system window border size. +const int kFrameBorderThickness = 4; +// Various edges of the frame border have a 1 px shadow along their edges; in a +// few cases we shift elements based on this amount for visual appeal. +const int kFrameShadowThickness = 1; +// While resize areas on Windows are normally the same size as the window +// borders, our top area is shrunk by 1 px to make it easier to move the window +// around with our thinner top grabbable strip. (Incidentally, our side and +// bottom resize areas don't match the frame border thickness either -- they +// span the whole nonclient area, so there's no "dead zone" for the mouse.) +const int kTopResizeAdjust = 1; +// In the window corners, the resize areas don't actually expand bigger, but the +// 16 px at the end of each edge triggers diagonal resizing. +const int kResizeAreaCornerSize = 16; +// The titlebar never shrinks too short to show the caption button plus some +// padding below it. +const int kCaptionButtonHeightWithPadding = 19; +// The titlebar has a 2 px 3D edge along the top and bottom. +const int kTitlebarTopAndBottomEdgeThickness = 2; +// The icon is inset 2 px from the left frame border. +const int kIconLeftSpacing = 2; +// The icon never shrinks below 16 px on a side. +const int kIconMinimumSize = 16; +// There is a 4 px gap between the icon and the title text. +const int kIconTitleSpacing = 4; +// There is a 5 px gap between the title text and the caption buttons. +const int kTitleCaptionSpacing = 5; +} + +/////////////////////////////////////////////////////////////////////////////// +// CustomFrameView, public: + +CustomFrameView::CustomFrameView(Widget* frame) + : ALLOW_THIS_IN_INITIALIZER_LIST(close_button_(new ImageButton(this))), + ALLOW_THIS_IN_INITIALIZER_LIST(restore_button_(new ImageButton(this))), + ALLOW_THIS_IN_INITIALIZER_LIST(maximize_button_(new ImageButton(this))), + ALLOW_THIS_IN_INITIALIZER_LIST(minimize_button_(new ImageButton(this))), + window_icon_(NULL), + should_show_minmax_buttons_(false), + should_show_client_edge_(false), + frame_(frame) { + InitClass(); + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + close_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); + + // Close button images will be set in LayoutWindowControls(). + AddChildView(close_button_); + + restore_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_RESTORE)); + restore_button_->SetImage(CustomButton::BS_NORMAL, + rb.GetBitmapNamed(IDR_RESTORE)); + restore_button_->SetImage(CustomButton::BS_HOT, + rb.GetBitmapNamed(IDR_RESTORE_H)); + restore_button_->SetImage(CustomButton::BS_PUSHED, + rb.GetBitmapNamed(IDR_RESTORE_P)); + AddChildView(restore_button_); + + maximize_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); + maximize_button_->SetImage(CustomButton::BS_NORMAL, + rb.GetBitmapNamed(IDR_MAXIMIZE)); + maximize_button_->SetImage(CustomButton::BS_HOT, + rb.GetBitmapNamed(IDR_MAXIMIZE_H)); + maximize_button_->SetImage(CustomButton::BS_PUSHED, + rb.GetBitmapNamed(IDR_MAXIMIZE_P)); + AddChildView(maximize_button_); + + minimize_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); + minimize_button_->SetImage(CustomButton::BS_NORMAL, + rb.GetBitmapNamed(IDR_MINIMIZE)); + minimize_button_->SetImage(CustomButton::BS_HOT, + rb.GetBitmapNamed(IDR_MINIMIZE_H)); + minimize_button_->SetImage(CustomButton::BS_PUSHED, + rb.GetBitmapNamed(IDR_MINIMIZE_P)); + AddChildView(minimize_button_); + + should_show_minmax_buttons_ = frame_->widget_delegate()->CanMaximize(); + should_show_client_edge_ = frame_->widget_delegate()->ShouldShowClientEdge(); + + if (frame_->widget_delegate()->ShouldShowWindowIcon()) { + window_icon_ = new ImageButton(this); + AddChildView(window_icon_); + } +} + +CustomFrameView::~CustomFrameView() { +} + +/////////////////////////////////////////////////////////////////////////////// +// CustomFrameView, NonClientFrameView implementation: + +gfx::Rect CustomFrameView::GetBoundsForClientView() const { + return client_view_bounds_; +} + +gfx::Rect CustomFrameView::GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const { + int top_height = NonClientTopBorderHeight(); + int border_thickness = NonClientBorderThickness(); + return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), + std::max(0, client_bounds.y() - top_height), + client_bounds.width() + (2 * border_thickness), + client_bounds.height() + top_height + border_thickness); +} + +int CustomFrameView::NonClientHitTest(const gfx::Point& point) { + // Sanity check. + if (!bounds().Contains(point)) + return HTNOWHERE; + + int frame_component = frame_->client_view()->NonClientHitTest(point); + + // See if we're in the sysmenu region. (We check the ClientView first to be + // consistent with OpaqueBrowserFrameView; it's not really necessary here.) + gfx::Rect sysmenu_rect(IconBounds()); + // In maximized mode we extend the rect to the screen corner to take advantage + // of Fitts' Law. + if (frame_->IsMaximized()) + sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom()); + sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect)); + if (sysmenu_rect.Contains(point)) + return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU; + + if (frame_component != HTNOWHERE) + return frame_component; + + // Then see if the point is within any of the window controls. + if (close_button_->GetMirroredBounds().Contains(point)) + return HTCLOSE; + if (restore_button_->GetMirroredBounds().Contains(point)) + return HTMAXBUTTON; + if (maximize_button_->GetMirroredBounds().Contains(point)) + return HTMAXBUTTON; + if (minimize_button_->GetMirroredBounds().Contains(point)) + return HTMINBUTTON; + if (window_icon_ && window_icon_->GetMirroredBounds().Contains(point)) + return HTSYSMENU; + + int window_component = GetHTComponentForFrame(point, FrameBorderThickness(), + NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, + frame_->widget_delegate()->CanResize()); + // Fall back to the caption if no other component matches. + return (window_component == HTNOWHERE) ? HTCAPTION : window_component; +} + +void CustomFrameView::GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) { + DCHECK(window_mask); + if (frame_->IsMaximized()) + return; + + views::GetDefaultWindowMask(size, window_mask); +} + +void CustomFrameView::EnableClose(bool enable) { + close_button_->SetEnabled(enable); +} + +void CustomFrameView::ResetWindowControls() { + restore_button_->SetState(CustomButton::BS_NORMAL); + minimize_button_->SetState(CustomButton::BS_NORMAL); + maximize_button_->SetState(CustomButton::BS_NORMAL); + // The close button isn't affected by this constraint. +} + +void CustomFrameView::UpdateWindowIcon() { + window_icon_->SchedulePaint(); +} + +/////////////////////////////////////////////////////////////////////////////// +// CustomFrameView, View overrides: + +void CustomFrameView::OnPaint(gfx::Canvas* canvas) { + if (frame_->IsMaximized()) + PaintMaximizedFrameBorder(canvas); + else + PaintRestoredFrameBorder(canvas); + PaintTitleBar(canvas); + if (ShouldShowClientEdge()) + PaintRestoredClientEdge(canvas); +} + +void CustomFrameView::Layout() { + LayoutWindowControls(); + LayoutTitleBar(); + LayoutClientView(); +} + +gfx::Size CustomFrameView::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(); +} + +/////////////////////////////////////////////////////////////////////////////// +// CustomFrameView, ButtonListener implementation: + +void CustomFrameView::ButtonPressed(Button* sender, const views::Event& event) { + if (sender == close_button_) + frame_->Close(); + else if (sender == minimize_button_) + frame_->Minimize(); + else if (sender == maximize_button_) + frame_->Maximize(); + else if (sender == restore_button_) + frame_->Restore(); +} + +/////////////////////////////////////////////////////////////////////////////// +// CustomFrameView, private: + +int CustomFrameView::FrameBorderThickness() const { + return frame_->IsMaximized() ? 0 : kFrameBorderThickness; +} + +int CustomFrameView::NonClientBorderThickness() const { + // In maximized mode, we don't show a client edge. + return FrameBorderThickness() + + (ShouldShowClientEdge() ? kClientEdgeThickness : 0); +} + +int CustomFrameView::NonClientTopBorderHeight() const { + return std::max(FrameBorderThickness() + IconSize(), + CaptionButtonY() + kCaptionButtonHeightWithPadding) + + TitlebarBottomThickness(); +} + +int CustomFrameView::CaptionButtonY() const { + // Maximized buttons start at window top so that even if their images aren't + // drawn flush with the screen edge, they still obey Fitts' Law. + return frame_->IsMaximized() ? FrameBorderThickness() : kFrameShadowThickness; +} + +int CustomFrameView::TitlebarBottomThickness() const { + return kTitlebarTopAndBottomEdgeThickness + + (ShouldShowClientEdge() ? kClientEdgeThickness : 0); +} + +int CustomFrameView::IconSize() const { +#if defined(OS_WIN) + // This metric scales up if either the titlebar height or the titlebar font + // size are increased. + return GetSystemMetrics(SM_CYSMICON); +#else + return std::max(title_font_->GetHeight(), kIconMinimumSize); +#endif +} + +bool CustomFrameView::ShouldShowClientEdge() const { + return should_show_client_edge_ && !frame_->IsMaximized(); +} + +gfx::Rect CustomFrameView::IconBounds() const { + int size = IconSize(); + int frame_thickness = FrameBorderThickness(); + // Our frame border has a different "3D look" than Windows'. Theirs has a + // more complex gradient on the top that they push their icon/title below; + // then the maximized window cuts this off and the icon/title are centered + // in the remaining space. Because the apparent shape of our border is + // simpler, using the same positioning makes things look slightly uncentered + // with restored windows, so when the window is restored, instead of + // calculating the remaining space from below the frame border, we calculate + // from below the 3D edge. + int unavailable_px_at_top = frame_->IsMaximized() ? + frame_thickness : kTitlebarTopAndBottomEdgeThickness; + // When the icon is shorter than the minimum space we reserve for the caption + // button, we vertically center it. We want to bias rounding to put extra + // space above the icon, since the 3D edge (+ client edge, for restored + // windows) below looks (to the eye) more like additional space than does the + // 3D edge (or nothing at all, for maximized windows) above; hence the +1. + int y = unavailable_px_at_top + (NonClientTopBorderHeight() - + unavailable_px_at_top - size - TitlebarBottomThickness() + 1) / 2; + return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size); +} + +void CustomFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { + // Window frame mode. + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + SkBitmap* frame_image; + SkColor frame_color; + if (frame_->IsActive()) { + frame_image = rb.GetBitmapNamed(IDR_FRAME); + frame_color = ResourceBundle::frame_color; + } else { + frame_image = rb.GetBitmapNamed(IDR_FRAME_INACTIVE); + frame_color = ResourceBundle::frame_color_inactive; + } + + SkBitmap* top_left_corner = rb.GetBitmapNamed(IDR_WINDOW_TOP_LEFT_CORNER); + SkBitmap* top_right_corner = + rb.GetBitmapNamed(IDR_WINDOW_TOP_RIGHT_CORNER); + SkBitmap* top_edge = rb.GetBitmapNamed(IDR_WINDOW_TOP_CENTER); + SkBitmap* right_edge = rb.GetBitmapNamed(IDR_WINDOW_RIGHT_SIDE); + SkBitmap* left_edge = rb.GetBitmapNamed(IDR_WINDOW_LEFT_SIDE); + SkBitmap* bottom_left_corner = + rb.GetBitmapNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER); + SkBitmap* bottom_right_corner = + rb.GetBitmapNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER); + SkBitmap* bottom_edge = rb.GetBitmapNamed(IDR_WINDOW_BOTTOM_CENTER); + + // Fill with the frame color first so we have a constant background for + // areas not covered by the theme image. + canvas->FillRect(frame_color, + gfx::Rect(0, 0, width(), frame_image->height())); + + int remaining_height = height() - frame_image->height(); + if (remaining_height > 0) { + // Now fill down the sides. + canvas->FillRect(frame_color, + gfx::Rect(0, frame_image->height(), left_edge->width(), + remaining_height)); + canvas->FillRect(frame_color, + gfx::Rect(width() - right_edge->width(), + frame_image->height(), right_edge->width(), + remaining_height)); + int center_width = width() - left_edge->width() - right_edge->width(); + if (center_width > 0) { + // Now fill the bottom area. + canvas->FillRect(frame_color, + gfx::Rect(left_edge->width(), + height() - bottom_edge->height(), + center_width, bottom_edge->height())); + } + } + + // Draw the theme frame. + canvas->TileImageInt(*frame_image, 0, 0, width(), frame_image->height()); + + // Top. + canvas->DrawBitmapInt(*top_left_corner, 0, 0); + canvas->TileImageInt(*top_edge, top_left_corner->width(), 0, + width() - top_right_corner->width(), top_edge->height()); + canvas->DrawBitmapInt(*top_right_corner, + width() - top_right_corner->width(), 0); + + // Right. + canvas->TileImageInt(*right_edge, width() - right_edge->width(), + top_right_corner->height(), right_edge->width(), + height() - top_right_corner->height() - bottom_right_corner->height()); + + // Bottom. + canvas->DrawBitmapInt(*bottom_right_corner, + width() - bottom_right_corner->width(), + height() - bottom_right_corner->height()); + canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(), + height() - bottom_edge->height(), + width() - bottom_left_corner->width() - bottom_right_corner->width(), + bottom_edge->height()); + canvas->DrawBitmapInt(*bottom_left_corner, 0, + height() - bottom_left_corner->height()); + + // Left. + canvas->TileImageInt(*left_edge, 0, top_left_corner->height(), + left_edge->width(), + height() - top_left_corner->height() - bottom_left_corner->height()); +} + +void CustomFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + SkBitmap* frame_image = rb.GetBitmapNamed(frame_->IsActive() ? + IDR_FRAME : IDR_FRAME_INACTIVE); + canvas->TileImageInt(*frame_image, 0, FrameBorderThickness(), width(), + frame_image->height()); + + // The bottom of the titlebar actually comes from the top of the Client Edge + // graphic, with the actual client edge clipped off the bottom. + SkBitmap* titlebar_bottom = rb.GetBitmapNamed(IDR_APP_TOP_CENTER); + int edge_height = titlebar_bottom->height() - + (ShouldShowClientEdge() ? kClientEdgeThickness : 0); + canvas->TileImageInt(*titlebar_bottom, 0, + frame_->client_view()->y() - edge_height, width(), edge_height); +} + +void CustomFrameView::PaintTitleBar(gfx::Canvas* canvas) { + WidgetDelegate* d = frame_->widget_delegate(); + + // It seems like in some conditions we can be asked to paint after the window + // that contains us is WM_DESTROYed. At this point, our delegate is NULL. The + // correct long term fix may be to shut down the RootView in WM_DESTROY. + if (!d) + return; + + canvas->DrawStringInt(d->GetWindowTitle(), *title_font_, + SK_ColorWHITE, GetMirroredXForRect(title_bounds_), + title_bounds_.y(), title_bounds_.width(), + title_bounds_.height()); +} + +void CustomFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { + gfx::Rect client_area_bounds = frame_->client_view()->bounds(); + int client_area_top = client_area_bounds.y(); + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + SkBitmap* top_left = rb.GetBitmapNamed(IDR_APP_TOP_LEFT); + SkBitmap* top = rb.GetBitmapNamed(IDR_APP_TOP_CENTER); + SkBitmap* top_right = rb.GetBitmapNamed(IDR_APP_TOP_RIGHT); + SkBitmap* right = rb.GetBitmapNamed(IDR_CONTENT_RIGHT_SIDE); + SkBitmap* bottom_right = + rb.GetBitmapNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER); + SkBitmap* bottom = rb.GetBitmapNamed(IDR_CONTENT_BOTTOM_CENTER); + SkBitmap* bottom_left = + rb.GetBitmapNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER); + SkBitmap* left = rb.GetBitmapNamed(IDR_CONTENT_LEFT_SIDE); + + // Top. + int top_edge_y = client_area_top - top->height(); + canvas->DrawBitmapInt(*top_left, client_area_bounds.x() - top_left->width(), + top_edge_y); + canvas->TileImageInt(*top, client_area_bounds.x(), top_edge_y, + client_area_bounds.width(), top->height()); + canvas->DrawBitmapInt(*top_right, client_area_bounds.right(), top_edge_y); + + // Right. + int client_area_bottom = + std::max(client_area_top, client_area_bounds.bottom()); + int client_area_height = client_area_bottom - client_area_top; + canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top, + right->width(), client_area_height); + + // Bottom. + canvas->DrawBitmapInt(*bottom_right, client_area_bounds.right(), + client_area_bottom); + canvas->TileImageInt(*bottom, client_area_bounds.x(), client_area_bottom, + client_area_bounds.width(), bottom_right->height()); + canvas->DrawBitmapInt(*bottom_left, + client_area_bounds.x() - bottom_left->width(), client_area_bottom); + + // Left. + canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), + client_area_top, left->width(), client_area_height); + + // Draw the toolbar color to fill in the edges. + canvas->DrawRectInt(ResourceBundle::toolbar_color, + client_area_bounds.x() - 1, client_area_top - 1, + client_area_bounds.width() + 1, client_area_bottom - client_area_top + 1); +} + +void CustomFrameView::LayoutWindowControls() { + close_button_->SetImageAlignment(ImageButton::ALIGN_LEFT, + ImageButton::ALIGN_BOTTOM); + int caption_y = CaptionButtonY(); + bool is_maximized = frame_->IsMaximized(); + // There should always be the same number of non-shadow pixels visible to the + // side of the caption buttons. In maximized mode we extend the rightmost + // button to the screen corner to obey Fitts' Law. + int right_extra_width = is_maximized ? + (kFrameBorderThickness - kFrameShadowThickness) : 0; + gfx::Size close_button_size = close_button_->GetPreferredSize(); + close_button_->SetBounds(width() - FrameBorderThickness() - + right_extra_width - close_button_size.width(), caption_y, + close_button_size.width() + right_extra_width, + close_button_size.height()); + + // When the window is restored, we show a maximized button; otherwise, we show + // a restore button. + bool is_restored = !is_maximized && !frame_->IsMinimized(); + views::ImageButton* invisible_button = is_restored ? + restore_button_ : maximize_button_; + invisible_button->SetVisible(false); + + views::ImageButton* visible_button = is_restored ? + maximize_button_ : restore_button_; + FramePartBitmap normal_part, hot_part, pushed_part; + if (should_show_minmax_buttons_) { + visible_button->SetVisible(true); + visible_button->SetImageAlignment(ImageButton::ALIGN_LEFT, + ImageButton::ALIGN_BOTTOM); + gfx::Size visible_button_size = visible_button->GetPreferredSize(); + visible_button->SetBounds(close_button_->x() - visible_button_size.width(), + caption_y, visible_button_size.width(), + visible_button_size.height()); + + minimize_button_->SetVisible(true); + minimize_button_->SetImageAlignment(ImageButton::ALIGN_LEFT, + ImageButton::ALIGN_BOTTOM); + gfx::Size minimize_button_size = minimize_button_->GetPreferredSize(); + minimize_button_->SetBounds( + visible_button->x() - minimize_button_size.width(), caption_y, + minimize_button_size.width(), + minimize_button_size.height()); + + normal_part = IDR_CLOSE; + hot_part = IDR_CLOSE_H; + pushed_part = IDR_CLOSE_P; + } else { + visible_button->SetVisible(false); + minimize_button_->SetVisible(false); + + normal_part = IDR_CLOSE_SA; + hot_part = IDR_CLOSE_SA_H; + pushed_part = IDR_CLOSE_SA_P; + } + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + close_button_->SetImage(CustomButton::BS_NORMAL, + rb.GetBitmapNamed(normal_part)); + close_button_->SetImage(CustomButton::BS_HOT, + rb.GetBitmapNamed(hot_part)); + close_button_->SetImage(CustomButton::BS_PUSHED, + rb.GetBitmapNamed(pushed_part)); +} + +void CustomFrameView::LayoutTitleBar() { + // The window title is based on the calculated icon position, even when there + // is no icon. + gfx::Rect icon_bounds(IconBounds()); + if (frame_->widget_delegate()->ShouldShowWindowIcon()) + window_icon_->SetBoundsRect(icon_bounds); + + // Size the title. + int title_x = frame_->widget_delegate()->ShouldShowWindowIcon() ? + icon_bounds.right() + kIconTitleSpacing : icon_bounds.x(); + int title_height = title_font_->GetHeight(); + // We bias the title position so that when the difference between the icon and + // title heights is odd, the extra pixel of the title is above the vertical + // midline rather than below. This compensates for how the icon is already + // biased downwards (see IconBounds()) and helps prevent descenders on the + // title from overlapping the 3D edge at the bottom of the titlebar. + title_bounds_.SetRect(title_x, + icon_bounds.y() + ((icon_bounds.height() - title_height - 1) / 2), + std::max(0, (should_show_minmax_buttons_ ? + minimize_button_->x() : close_button_->x()) - kTitleCaptionSpacing - + title_x), title_height); +} + +void CustomFrameView::LayoutClientView() { + int top_height = NonClientTopBorderHeight(); + int border_thickness = NonClientBorderThickness(); + client_view_bounds_.SetRect(border_thickness, top_height, + std::max(0, width() - (2 * border_thickness)), + std::max(0, height() - top_height - border_thickness)); +} + +// static +void CustomFrameView::InitClass() { + static bool initialized = false; + if (!initialized) { +#if defined(USE_AURA) + title_font_ = new gfx::Font(NativeWidgetAura::GetWindowTitleFont()); +#elif defined(OS_WIN) + title_font_ = new gfx::Font(NativeWidgetWin::GetWindowTitleFont()); +#elif defined(OS_LINUX) + // TODO(ben): need to resolve what font this is. + title_font_ = new gfx::Font(); +#endif + initialized = true; + } +} + +} // namespace views diff --git a/ui/views/window/custom_frame_view.h b/ui/views/window/custom_frame_view.h new file mode 100644 index 0000000..8a04dd4 --- /dev/null +++ b/ui/views/window/custom_frame_view.h @@ -0,0 +1,127 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_CUSTOM_FRAME_VIEW_H_ +#define UI_VIEWS_WINDOW_CUSTOM_FRAME_VIEW_H_ +#pragma once + +#include "views/controls/button/image_button.h" +#include "views/widget/widget.h" +#include "views/window/non_client_view.h" + +namespace gfx { +class Canvas; +class Font; +class Size; +class Path; +class Point; +} + +namespace views { + +/////////////////////////////////////////////////////////////////////////////// +// +// CustomFrameView +// +// A ChromeView that provides the non client frame for Windows. This means +// rendering the non-standard window caption, border, and controls. +// +//////////////////////////////////////////////////////////////////////////////// +class CustomFrameView : public NonClientFrameView, + public ButtonListener { + public: + explicit CustomFrameView(Widget* frame); + virtual ~CustomFrameView(); + + // Overridden from NonClientFrameView: + 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 EnableClose(bool enable) OVERRIDE; + virtual void ResetWindowControls() OVERRIDE; + virtual void UpdateWindowIcon() OVERRIDE; + + // View overrides: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void Layout() OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + + // ButtonListener implementation: + virtual void ButtonPressed(Button* sender, const views::Event& event) + OVERRIDE; + + private: + // Returns the thickness of the border that makes up the window frame edges. + // This does not include any client edge. + int FrameBorderThickness() const; + + // Returns the thickness of the entire nonclient left, right, and bottom + // borders, including both the window frame and any client edge. + int NonClientBorderThickness() const; + + // Returns the height of the entire nonclient top border, including the window + // frame, any title area, and any connected client edge. + int NonClientTopBorderHeight() const; + + // Returns the y-coordinate of the caption buttons. + int CaptionButtonY() const; + + // Returns the thickness of the nonclient portion of the 3D edge along the + // bottom of the titlebar. + int TitlebarBottomThickness() const; + + // Returns the size of the titlebar icon. This is used even when the icon is + // not shown, e.g. to set the titlebar height. + int IconSize() const; + + // Returns the bounds of the titlebar icon (or where the icon would be if + // there was one). + gfx::Rect IconBounds() const; + + // Returns true if the client edge should be drawn. This is true if + // the window delegate wants a client edge and we are not maxmized. + bool ShouldShowClientEdge() const; + + // Paint various sub-components of this view. + void PaintRestoredFrameBorder(gfx::Canvas* canvas); + void PaintMaximizedFrameBorder(gfx::Canvas* canvas); + void PaintTitleBar(gfx::Canvas* canvas); + void PaintRestoredClientEdge(gfx::Canvas* canvas); + + // Layout various sub-components of this view. + void LayoutWindowControls(); + void LayoutTitleBar(); + void LayoutClientView(); + + // The bounds of the client view, in this view's coordinates. + gfx::Rect client_view_bounds_; + + // The layout rect of the title, if visible. + gfx::Rect title_bounds_; + + // Window controls. + ImageButton* close_button_; + ImageButton* restore_button_; + ImageButton* maximize_button_; + ImageButton* minimize_button_; + ImageButton* window_icon_; + bool should_show_minmax_buttons_; + bool should_show_client_edge_; + + // The window that owns this view. + Widget* frame_; + + // Initialize various static resources. + static void InitClass(); + static gfx::Font* title_font_; + + DISALLOW_COPY_AND_ASSIGN(CustomFrameView); +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_CUSTOM_FRAME_VIEW_H_ diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc new file mode 100644 index 0000000..f0abe6f --- /dev/null +++ b/ui/views/window/dialog_client_view.cc @@ -0,0 +1,591 @@ +// Copyright (c) 2011 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 "views/window/dialog_client_view.h" + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#include <uxtheme.h> +#include <vsstyle.h> +#elif defined(TOOLKIT_USES_GTK) +#include <gtk/gtk.h> +#endif + +#include <algorithm> + +#include "base/utf_string_conversions.h" +#include "grit/ui_strings.h" +#include "ui/base/hit_test.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/font.h" +#include "views/controls/button/text_button.h" +#include "views/layout/layout_constants.h" +#include "views/widget/root_view.h" +#include "views/widget/widget.h" +#include "views/window/dialog_delegate.h" + +#if defined(OS_WIN) +#include "ui/gfx/native_theme.h" +#else +#include "ui/gfx/skia_utils_gtk.h" +#endif + +namespace views { +namespace { + +// Updates any of the standard buttons according to the delegate. +void UpdateButtonHelper(NativeTextButton* button_view, + DialogDelegate* delegate, + ui::DialogButton button) { + string16 label = delegate->GetDialogButtonLabel(button); + if (!label.empty()) + button_view->SetText(label); + button_view->SetEnabled(delegate->IsDialogButtonEnabled(button)); + button_view->SetVisible(delegate->IsDialogButtonVisible(button)); +} + +#if defined(OS_WIN) +void FillViewWithSysColor(gfx::Canvas* canvas, View* view, COLORREF color) { + SkColor sk_color = + SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color)); + canvas->FillRect(sk_color, view->GetLocalBounds()); +} +#endif + +// DialogButton ---------------------------------------------------------------- + +// DialogButtons is used for the ok/cancel buttons of the window. DialogButton +// forwards AcceleratorPressed to the delegate. + +class DialogButton : public NativeTextButton { + public: + DialogButton(ButtonListener* listener, + Widget* owner, + ui::DialogButton type, + const string16& title, + bool is_default) + : NativeTextButton(listener, title), + owner_(owner), + type_(type) { + SetIsDefault(is_default); + } + + // Overridden to forward to the delegate. + virtual bool AcceleratorPressed(const Accelerator& accelerator) { + if (!owner_->widget_delegate()->AsDialogDelegate()-> + AreAcceleratorsEnabled(type_)) { + return false; + } + return NativeTextButton::AcceleratorPressed(accelerator); + } + + private: + Widget* owner_; + const ui::DialogButton type_; + + DISALLOW_COPY_AND_ASSIGN(DialogButton); +}; + +} // namespace + +// static +gfx::Font* DialogClientView::dialog_button_font_ = NULL; +static const int kDialogMinButtonWidth = 75; +static const int kDialogButtonLabelSpacing = 16; +static const int kDialogButtonContentSpacing = 5; + +// The group used by the buttons. This name is chosen voluntarily big not to +// conflict with other groups that could be in the dialog content. +static const int kButtonGroup = 6666; + +/////////////////////////////////////////////////////////////////////////////// +// DialogClientView, public: + +DialogClientView::DialogClientView(Widget* owner, View* contents_view) + : ClientView(owner, contents_view), + ok_button_(NULL), + cancel_button_(NULL), + default_button_(NULL), + extra_view_(NULL), + size_extra_view_height_to_buttons_(false), + notified_delegate_(false), + listening_to_focus_(false), + saved_focus_manager_(NULL), + bottom_view_(NULL) { + InitClass(); +} + +DialogClientView::~DialogClientView() { +} + +void DialogClientView::ShowDialogButtons() { + DialogDelegate* dd = GetDialogDelegate(); + int buttons = dd->GetDialogButtons(); + if (buttons & ui::DIALOG_BUTTON_OK && !ok_button_) { + string16 label = dd->GetDialogButtonLabel(ui::DIALOG_BUTTON_OK); + if (label.empty()) + label = l10n_util::GetStringUTF16(IDS_APP_OK); + bool is_default_button = + (dd->GetDefaultDialogButton() & ui::DIALOG_BUTTON_OK) != 0; + ok_button_ = new DialogButton(this, + GetWidget(), + ui::DIALOG_BUTTON_OK, + label, + is_default_button); + ok_button_->SetGroup(kButtonGroup); + if (is_default_button) + default_button_ = ok_button_; + if (!(buttons & ui::DIALOG_BUTTON_CANCEL)) + ok_button_->AddAccelerator(Accelerator(ui::VKEY_ESCAPE, + false, false, false)); + AddChildView(ok_button_); + } + if (buttons & ui::DIALOG_BUTTON_CANCEL && !cancel_button_) { + string16 label = + dd->GetDialogButtonLabel(ui::DIALOG_BUTTON_CANCEL); + if (label.empty()) { + if (buttons & ui::DIALOG_BUTTON_OK) { + label = l10n_util::GetStringUTF16(IDS_APP_CANCEL); + } else { + label = l10n_util::GetStringUTF16(IDS_APP_CLOSE); + } + } + bool is_default_button = + (dd->GetDefaultDialogButton() & ui::DIALOG_BUTTON_CANCEL) + != 0; + cancel_button_ = new DialogButton(this, + GetWidget(), + ui::DIALOG_BUTTON_CANCEL, + label, + is_default_button); + cancel_button_->SetGroup(kButtonGroup); + cancel_button_->AddAccelerator(Accelerator(ui::VKEY_ESCAPE, + false, false, false)); + if (is_default_button) + default_button_ = ok_button_; + AddChildView(cancel_button_); + } + if (!buttons) { + // Register the escape key as an accelerator which will close the window + // if there are no dialog buttons. + AddAccelerator(Accelerator(ui::VKEY_ESCAPE, false, false, false)); + } +} + +void DialogClientView::SetDefaultButton(NativeTextButton* new_default_button) { + if (default_button_ && default_button_ != new_default_button) { + default_button_->SetIsDefault(false); + default_button_ = NULL; + } + + if (new_default_button) { + default_button_ = new_default_button; + default_button_->SetIsDefault(true); + } +} + +void DialogClientView::OnWillChangeFocus(View* focused_before, + View* focused_now) { + NativeTextButton* new_default_button = NULL; + if (focused_now && + focused_now->GetClassName() == NativeTextButton::kViewClassName) { + new_default_button = static_cast<NativeTextButton*>(focused_now); + } else { + // The focused view is not a button, get the default button from the + // delegate. + DialogDelegate* dd = GetDialogDelegate(); + if ((dd->GetDefaultDialogButton() & ui::DIALOG_BUTTON_OK) != 0) + new_default_button = ok_button_; + if ((dd->GetDefaultDialogButton() & ui::DIALOG_BUTTON_CANCEL) + != 0) + new_default_button = cancel_button_; + } + SetDefaultButton(new_default_button); +} + +void DialogClientView::OnDidChangeFocus(View* focused_before, + View* focused_now) { +} + +// Changing dialog labels will change button widths. +void DialogClientView::UpdateDialogButtons() { + DialogDelegate* dd = GetDialogDelegate(); + int buttons = dd->GetDialogButtons(); + + if (buttons & ui::DIALOG_BUTTON_OK) + UpdateButtonHelper(ok_button_, dd, ui::DIALOG_BUTTON_OK); + + if (buttons & ui::DIALOG_BUTTON_CANCEL) { + UpdateButtonHelper(cancel_button_, dd, ui::DIALOG_BUTTON_CANCEL); + } + + LayoutDialogButtons(); + SchedulePaint(); +} + +void DialogClientView::AcceptWindow() { + if (notified_delegate_) { + // Only notify the delegate once. See comment in header above + // notified_delegate_ for details. + return; + } + if (GetDialogDelegate()->Accept(false)) { + notified_delegate_ = true; + Close(); + } +} + +void DialogClientView::CancelWindow() { + // Call the standard Close handler, which checks with the delegate before + // proceeding. This checking _isn't_ done here, but in the WM_CLOSE handler, + // so that the close box on the window also shares this code path. + Close(); +} + +void DialogClientView::SetBottomView(View* bottom_view) { + if (bottom_view_) { + RemoveChildView(bottom_view_); + delete bottom_view_; + } + bottom_view_ = bottom_view; + if (bottom_view_) + AddChildView(bottom_view_); +} + +/////////////////////////////////////////////////////////////////////////////// +// DialogClientView, View overrides: + +void DialogClientView::NativeViewHierarchyChanged( + bool attached, + gfx::NativeView native_view, + internal::RootView* root_view) { + if (attached) { + UpdateFocusListener(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// DialogClientView, ClientView overrides: + +bool DialogClientView::CanClose() { + if (notified_delegate_) + return true; + + DialogDelegate* dd = GetDialogDelegate(); + int buttons = dd->GetDialogButtons(); + bool close = true; + if (buttons & ui::DIALOG_BUTTON_CANCEL) + close = dd->Cancel(); + else if (buttons & ui::DIALOG_BUTTON_OK) + close = dd->Accept(true); + notified_delegate_ = close; + return close; +} + +void DialogClientView::WidgetClosing() { + if (listening_to_focus_) { + DCHECK(saved_focus_manager_); + if (saved_focus_manager_) + saved_focus_manager_->RemoveFocusChangeListener(this); + } +} + +int DialogClientView::NonClientHitTest(const gfx::Point& point) { + if (size_box_bounds_.Contains(point.x() - x(), point.y() - y())) + return HTBOTTOMRIGHT; + return ClientView::NonClientHitTest(point); +} + +DialogClientView* DialogClientView::AsDialogClientView() { + return this; +} + +const DialogClientView* DialogClientView::AsDialogClientView() const { + return this; +} + +//////////////////////////////////////////////////////////////////////////////// +// DialogClientView, View overrides: + +void DialogClientView::OnPaint(gfx::Canvas* canvas) { +#if defined(OS_WIN) + FillViewWithSysColor(canvas, this, GetSysColor(COLOR_3DFACE)); +#elif defined(USE_WAYLAND) || defined(USE_AURA) + SkColor sk_color = SkColorSetARGB(200, 255, 255, 255); + canvas->FillRect(sk_color, GetLocalBounds()); +#else + GtkWidget* widget = GetWidget()->GetNativeView(); + if (GTK_IS_WINDOW(widget)) { + GtkStyle* window_style = gtk_widget_get_style(widget); + canvas->FillRect(gfx::GdkColorToSkColor(window_style->bg[GTK_STATE_NORMAL]), + GetLocalBounds()); + } +#endif +} + +void DialogClientView::PaintChildren(gfx::Canvas* canvas) { + View::PaintChildren(canvas); + if (!GetWidget()->IsMaximized() && !GetWidget()->IsMinimized()) + PaintSizeBox(canvas); +} + +void DialogClientView::Layout() { + if (has_dialog_buttons()) + LayoutDialogButtons(); + if (bottom_view_) { + gfx::Rect bounds = GetContentsBounds(); + gfx::Size pref = bottom_view_->GetPreferredSize(); + bottom_view_->SetBounds(bounds.x(), + bounds.bottom() - pref.height() - kButtonVEdgeMargin, + bounds.width(), pref.height()); + } + LayoutContentsView(); +} + +void DialogClientView::ViewHierarchyChanged(bool is_add, View* parent, + View* child) { + if (is_add && child == this) { + // Can only add and update the dialog buttons _after_ they are added to the + // view hierarchy since they are native controls and require the + // Container's HWND. + ShowDialogButtons(); + ClientView::ViewHierarchyChanged(is_add, parent, child); + + UpdateFocusListener(); + + // The "extra view" must be created and installed after the contents view + // has been inserted into the view hierarchy. + CreateExtraView(); + UpdateDialogButtons(); + Layout(); + } +} + +gfx::Size DialogClientView::GetPreferredSize() { + gfx::Size prefsize = contents_view()->GetPreferredSize(); + int button_height = 0; + if (has_dialog_buttons()) { + if (cancel_button_) + button_height = cancel_button_->height(); + else + button_height = ok_button_->height(); + // Account for padding above and below the button. + button_height += kDialogButtonContentSpacing + kButtonVEdgeMargin; + + // Make sure the view is sized to the buttons's width if they are wider than + // the contents. + int width = 0; + if (cancel_button_) + width += GetButtonWidth(ui::DIALOG_BUTTON_CANCEL); + if (ok_button_) { + width += GetButtonWidth(ui::DIALOG_BUTTON_OK); + if (cancel_button_) + width += kRelatedButtonHSpacing; + } + if (extra_view_) { + width += extra_view_->GetPreferredSize().width(); + if (cancel_button_ || ok_button_) + width += kRelatedButtonHSpacing; + } + if (width > 0) { + width += 2 * kButtonHEdgeMargin; + prefsize.set_width(std::max(prefsize.width(), width)); + } + } + if (bottom_view_) { + gfx::Size bottom_pref = bottom_view_->GetPreferredSize(); + prefsize.Enlarge(0, bottom_pref.height() + kButtonVEdgeMargin); + } + prefsize.Enlarge(0, button_height); + return prefsize; +} + +bool DialogClientView::AcceleratorPressed(const Accelerator& accelerator) { + // We only expect Escape key. + DCHECK(accelerator.key_code() == ui::VKEY_ESCAPE); + Close(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// DialogClientView, ButtonListener implementation: + +void DialogClientView::ButtonPressed( + Button* sender, const views::Event& event) { + // We NULL check the delegate here since the buttons can receive WM_COMMAND + // messages even after they (and the window containing us) are destroyed. + if (!GetDialogDelegate()) + return; + + if (sender == ok_button_) { + AcceptWindow(); + } else if (sender == cancel_button_) { + CancelWindow(); + } else { + NOTREACHED(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DialogClientView, private: + +void DialogClientView::PaintSizeBox(gfx::Canvas* canvas) { + if (GetWidget()->widget_delegate()->CanResize() || + GetWidget()->widget_delegate()->CanMaximize()) { +#if defined(OS_WIN) + gfx::NativeTheme::ExtraParams extra; + gfx::Size gripper_size = gfx::NativeTheme::instance()->GetPartSize( + gfx::NativeTheme::kWindowResizeGripper, gfx::NativeTheme::kNormal, + extra); + + // TODO(beng): (http://b/1085509) In "classic" rendering mode, there isn't + // a theme-supplied gripper. We should probably improvise + // something, which would also require changing |gripper_size| + // to have different default values, too... + size_box_bounds_ = GetContentsBounds(); + size_box_bounds_.set_x(size_box_bounds_.right() - gripper_size.width()); + size_box_bounds_.set_y(size_box_bounds_.bottom() - gripper_size.height()); + + gfx::NativeTheme::instance()->Paint(canvas->GetSkCanvas(), + gfx::NativeTheme::kWindowResizeGripper, + gfx::NativeTheme::kNormal, + size_box_bounds_, + extra); +#else + NOTIMPLEMENTED(); + // TODO(port): paint size box +#endif + } +} + +int DialogClientView::GetButtonWidth(int button) const { + DialogDelegate* dd = GetDialogDelegate(); + string16 button_label = dd->GetDialogButtonLabel( + static_cast<ui::DialogButton>(button)); + int string_width = dialog_button_font_->GetStringWidth(button_label); + return std::max(string_width + kDialogButtonLabelSpacing, + kDialogMinButtonWidth); +} + +int DialogClientView::GetButtonsHeight() const { + if (has_dialog_buttons()) { + if (cancel_button_) + return cancel_button_->height() + kDialogButtonContentSpacing; + return ok_button_->height() + kDialogButtonContentSpacing; + } + return 0; +} + +void DialogClientView::LayoutDialogButtons() { + gfx::Rect lb = GetContentsBounds(); + gfx::Rect extra_bounds; + int bottom_y = lb.bottom() - kButtonVEdgeMargin; + int button_height = 0; + if (bottom_view_) { + gfx::Size bottom_pref = bottom_view_->GetPreferredSize(); + bottom_y -= bottom_pref.height() + kButtonVEdgeMargin + kButtonVEdgeMargin; + } + if (cancel_button_) { + gfx::Size ps = cancel_button_->GetPreferredSize(); + int button_width = std::max( + GetButtonWidth(ui::DIALOG_BUTTON_CANCEL), ps.width()); + int button_x = lb.right() - button_width - kButtonHEdgeMargin; + int button_y = bottom_y - ps.height(); + cancel_button_->SetBounds(button_x, button_y, button_width, ps.height()); + // The extra view bounds are dependent on this button. + extra_bounds.set_width(std::max(0, cancel_button_->x())); + extra_bounds.set_y(cancel_button_->y()); + button_height = std::max(button_height, ps.height()); + } + if (ok_button_) { + gfx::Size ps = ok_button_->GetPreferredSize(); + int button_width = std::max( + GetButtonWidth(ui::DIALOG_BUTTON_OK), ps.width()); + int ok_button_right = lb.right() - kButtonHEdgeMargin; + if (cancel_button_) + ok_button_right = cancel_button_->x() - kRelatedButtonHSpacing; + int button_x = ok_button_right - button_width; + int button_y = bottom_y - ps.height(); + ok_button_->SetBounds(button_x, button_y, ok_button_right - button_x, + ps.height()); + // The extra view bounds are dependent on this button. + extra_bounds.set_width(std::max(0, ok_button_->x())); + extra_bounds.set_y(ok_button_->y()); + button_height = std::max(button_height, ps.height()); + } + if (extra_view_) { + gfx::Size ps = extra_view_->GetPreferredSize(); + extra_bounds.set_x(lb.x() + kButtonHEdgeMargin); + int height = size_extra_view_height_to_buttons_ ? + std::max(ps.height(), button_height) : ps.height(); + extra_bounds.set_height(height); + extra_view_->SetBoundsRect(extra_bounds); + } +} + +void DialogClientView::LayoutContentsView() { + gfx::Rect lb = GetContentsBounds(); + lb.set_height(std::max(0, lb.height() - GetButtonsHeight())); + contents_view()->SetBoundsRect(lb); + contents_view()->Layout(); +} + +void DialogClientView::CreateExtraView() { + View* extra_view = GetDialogDelegate()->GetExtraView(); + if (extra_view && !extra_view_) { + extra_view_ = extra_view; + extra_view_->SetGroup(kButtonGroup); + AddChildView(extra_view_); + size_extra_view_height_to_buttons_ = + GetDialogDelegate()->GetSizeExtraViewHeightToButtons(); + } +} + +DialogDelegate* DialogClientView::GetDialogDelegate() const { + return GetWidget()->widget_delegate()->AsDialogDelegate(); +} + +void DialogClientView::Close() { + GetWidget()->Close(); + GetDialogDelegate()->OnClose(); +} + +void DialogClientView::UpdateFocusListener() { + FocusManager* focus_manager = GetFocusManager(); + // Listen for focus change events so we can update the default button. + // focus_manager can be NULL when the dialog is created on un-shown view. + // We start listening for focus changes when the page is visible. + // Focus manager could also change if window host changes a parent. + if (listening_to_focus_) { + if (saved_focus_manager_ == focus_manager) + return; + DCHECK(saved_focus_manager_); + if (saved_focus_manager_) + saved_focus_manager_->RemoveFocusChangeListener(this); + listening_to_focus_ = false; + } + saved_focus_manager_ = focus_manager; + // Listen for focus change events so we can update the default button. + if (focus_manager) { + focus_manager->AddFocusChangeListener(this); + listening_to_focus_ = true; + } +} + +// static +void DialogClientView::InitClass() { + static bool initialized = false; + if (!initialized) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + dialog_button_font_ = new gfx::Font(rb.GetFont(ResourceBundle::BaseFont)); + initialized = true; + } +} + +} // namespace views diff --git a/ui/views/window/dialog_client_view.h b/ui/views/window/dialog_client_view.h new file mode 100644 index 0000000..093815b --- /dev/null +++ b/ui/views/window/dialog_client_view.h @@ -0,0 +1,170 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_DIALOG_CLIENT_VIEW_H_ +#define UI_VIEWS_WINDOW_DIALOG_CLIENT_VIEW_H_ +#pragma once + +#include "ui/gfx/font.h" +#include "views/focus/focus_manager.h" +#include "views/controls/button/button.h" +#include "views/window/client_view.h" + +namespace views { + +class DialogDelegate; +class NativeTextButton; +class Widget; +namespace internal { +class RootView; +} + +/////////////////////////////////////////////////////////////////////////////// +// DialogClientView +// +// This ClientView subclass provides the content of a typical dialog box, +// including a strip of buttons at the bottom right of the window, default +// accelerator handlers for accept and cancel, and the ability for the +// embedded contents view to provide extra UI to be shown in the row of +// buttons. +// +// DialogClientView also provides the ability to set an arbitrary view that is +// positioned beneath the buttons. +// +class VIEWS_EXPORT DialogClientView : public ClientView, + public ButtonListener, + public FocusChangeListener { + public: + DialogClientView(Widget* widget, View* contents_view); + virtual ~DialogClientView(); + + // Adds the dialog buttons required by the supplied DialogDelegate to the + // view. + void ShowDialogButtons(); + + // Updates the enabled state and label of the buttons required by the + // supplied DialogDelegate + void UpdateDialogButtons(); + + // Accept the changes made in the window that contains this ClientView. + void AcceptWindow(); + + // Cancel the changes made in the window that contains this ClientView. + void CancelWindow(); + + // Accessors in case the user wishes to adjust these buttons. + NativeTextButton* ok_button() const { return ok_button_; } + NativeTextButton* cancel_button() const { return cancel_button_; } + + // Sets the view that is positioned along the bottom of the buttons. The + // bottom view is positioned beneath the buttons at the full width of the + // dialog. If there is an existing bottom view it is removed and deleted. + void SetBottomView(View* bottom_view); + + // Overridden from View: + virtual void NativeViewHierarchyChanged( + bool attached, + gfx::NativeView native_view, + internal::RootView* root_view) OVERRIDE; + + // Overridden from ClientView: + virtual bool CanClose() OVERRIDE; + virtual void WidgetClosing() OVERRIDE; + virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE; + virtual DialogClientView* AsDialogClientView() OVERRIDE; + virtual const DialogClientView* AsDialogClientView() const OVERRIDE; + + // FocusChangeListener implementation: + virtual void OnWillChangeFocus(View* focused_before, + View* focused_now) OVERRIDE; + virtual void OnDidChangeFocus(View* focused_before, + View* focused_now) OVERRIDE; + + protected: + // View overrides: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE; + virtual void Layout() OVERRIDE; + virtual void ViewHierarchyChanged(bool is_add, View* parent, + View* child) OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual bool AcceleratorPressed(const Accelerator& accelerator) OVERRIDE; + + // ButtonListener implementation: + virtual void ButtonPressed(Button* sender, + const views::Event& event) OVERRIDE; + + private: + // Paint the size box in the bottom right corner of the window if it is + // resizable. + void PaintSizeBox(gfx::Canvas* canvas); + + // Returns the width of the specified dialog button using the correct font. + int GetButtonWidth(int button) const; + int GetButtonsHeight() const; + + // Position and size various sub-views. + void LayoutDialogButtons(); + void LayoutContentsView(); + + // Makes the specified button the default button. + void SetDefaultButton(NativeTextButton* button); + + bool has_dialog_buttons() const { return ok_button_ || cancel_button_; } + + // Create and add the extra view, if supplied by the delegate. + void CreateExtraView(); + + // Returns the DialogDelegate for the window. + DialogDelegate* GetDialogDelegate() const; + + // Closes the widget. + void Close(); + + // Updates focus listener. + void UpdateFocusListener(); + + static void InitClass(); + + // The dialog buttons. + NativeTextButton* ok_button_; + NativeTextButton* cancel_button_; + + // The button that is currently the default button if any. + NativeTextButton* default_button_; + + // The button-level extra view, NULL unless the dialog delegate supplies one. + View* extra_view_; + + // See description of DialogDelegate::GetSizeExtraViewHeightToButtons for + // details on this. + bool size_extra_view_height_to_buttons_; + + // The layout rect of the size box, when visible. + gfx::Rect size_box_bounds_; + + // True if we've notified the delegate the window is closing and the delegate + // allosed the close. In some situations it's possible to get two closes (see + // http://crbug.com/71940). This is used to avoid notifying the delegate + // twice, which can have bad consequences. + bool notified_delegate_; + + // true if focus listener is added. + bool listening_to_focus_; + + // When ancestor gets changed focus manager gets changed as well. + FocusManager* saved_focus_manager_; + + // View positioned along the bottom, beneath the buttons. + View* bottom_view_; + + // Static resource initialization + static gfx::Font* dialog_button_font_; + + DISALLOW_COPY_AND_ASSIGN(DialogClientView); +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_DIALOG_CLIENT_VIEW_H_ diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc new file mode 100644 index 0000000..fc24f90 --- /dev/null +++ b/ui/views/window/dialog_delegate.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2011 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 "views/window/dialog_delegate.h" + +#include "base/logging.h" +#include "views/controls/button/text_button.h" +#include "views/widget/widget.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// DialogDelegate: + +DialogDelegate* DialogDelegate::AsDialogDelegate() { return this; } + +int DialogDelegate::GetDialogButtons() const { + return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL; +} + +int DialogDelegate::GetDefaultDialogButton() const { + if (GetDialogButtons() & ui::DIALOG_BUTTON_OK) + return ui::DIALOG_BUTTON_OK; + if (GetDialogButtons() & ui::DIALOG_BUTTON_CANCEL) + return ui::DIALOG_BUTTON_CANCEL; + return ui::DIALOG_BUTTON_NONE; +} + +string16 DialogDelegate::GetDialogButtonLabel(ui::DialogButton button) const { + // Empty string results in defaults for + // ui::DIALOG_BUTTON_OK or ui::DIALOG_BUTTON_CANCEL. + return string16(); +} + +bool DialogDelegate::IsDialogButtonEnabled(ui::DialogButton button) const { + return true; +} + +bool DialogDelegate::IsDialogButtonVisible(ui::DialogButton button) const { + return true; +} + +bool DialogDelegate::AreAcceleratorsEnabled(ui::DialogButton button) { + return true; +} + +View* DialogDelegate::GetExtraView() { + return NULL; +} + +bool DialogDelegate::GetSizeExtraViewHeightToButtons() { + return false; +} + +bool DialogDelegate::Cancel() { + return true; +} + +bool DialogDelegate::Accept(bool window_closiang) { + return Accept(); +} + +bool DialogDelegate::Accept() { + return true; +} + +View* DialogDelegate::GetInitiallyFocusedView() { + // Focus the default button if any. + const DialogClientView* dcv = GetDialogClientView(); + int default_button = GetDefaultDialogButton(); + if (default_button == ui::DIALOG_BUTTON_NONE) + return NULL; + + if ((default_button & GetDialogButtons()) == 0) { + // The default button is a button we don't have. + NOTREACHED(); + return NULL; + } + + if (default_button & ui::DIALOG_BUTTON_OK) + return dcv->ok_button(); + if (default_button & ui::DIALOG_BUTTON_CANCEL) + return dcv->cancel_button(); + return NULL; +} + +ClientView* DialogDelegate::CreateClientView(Widget* widget) { + return new DialogClientView(widget, GetContentsView()); +} + +const DialogClientView* DialogDelegate::GetDialogClientView() const { + return GetWidget()->client_view()->AsDialogClientView(); +} + +DialogClientView* DialogDelegate::GetDialogClientView() { + return GetWidget()->client_view()->AsDialogClientView(); +} + +ui::AccessibilityTypes::Role DialogDelegate::GetAccessibleWindowRole() const { + return ui::AccessibilityTypes::ROLE_DIALOG; +} + +//////////////////////////////////////////////////////////////////////////////// +// DialogDelegateView: + +DialogDelegateView::DialogDelegateView() { +} + +DialogDelegateView::~DialogDelegateView() { +} + +Widget* DialogDelegateView::GetWidget() { + return View::GetWidget(); +} + +const Widget* DialogDelegateView::GetWidget() const { + return View::GetWidget(); +} + +} // namespace views diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h new file mode 100644 index 0000000..75b9d5f --- /dev/null +++ b/ui/views/window/dialog_delegate.h @@ -0,0 +1,128 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_DIALOG_DELEGATE_H_ +#define UI_VIEWS_WINDOW_DIALOG_DELEGATE_H_ +#pragma once + +#include "base/string16.h" +#include "ui/base/accessibility/accessibility_types.h" +#include "ui/base/ui_base_types.h" +#include "views/widget/widget_delegate.h" +#include "views/window/dialog_client_view.h" + +namespace views { + +class View; + +/////////////////////////////////////////////////////////////////////////////// +// +// DialogDelegate +// +// DialogDelegate is an interface implemented by objects that wish to show a +// dialog box Window. The window that is displayed uses this interface to +// determine how it should be displayed and notify the delegate object of +// certain events. +// +/////////////////////////////////////////////////////////////////////////////// +class VIEWS_EXPORT DialogDelegate : public WidgetDelegate { + public: + virtual DialogDelegate* AsDialogDelegate(); + + // Returns a mask specifying which of the available DialogButtons are visible + // for the dialog. Note: If an OK button is provided, you should provide a + // CANCEL button. A dialog box with just an OK button is frowned upon and + // considered a very special case, so if you're planning on including one, + // you should reconsider, or beng says there will be stabbings. + // + // To use the extra button you need to override GetDialogButtons() + virtual int GetDialogButtons() const; + + // Returns the default dialog button. This should not be a mask as only + // one button should ever be the default button. Return + // ui::DIALOG_BUTTON_NONE if there is no default. Default + // behavior is to return ui::DIALOG_BUTTON_OK or + // ui::DIALOG_BUTTON_CANCEL (in that order) if they are + // present, ui::DIALOG_BUTTON_NONE otherwise. + virtual int GetDefaultDialogButton() const; + + // Returns the label of the specified dialog button. + virtual string16 GetDialogButtonLabel(ui::DialogButton button) const; + + // Returns whether the specified dialog button is enabled. + virtual bool IsDialogButtonEnabled(ui::DialogButton button) const; + + // Returns whether the specified dialog button is visible. + virtual bool IsDialogButtonVisible(ui::DialogButton button) const; + + // Returns whether accelerators are enabled on the button. This is invoked + // when an accelerator is pressed, not at construction time. This + // returns true. + virtual bool AreAcceleratorsEnabled(ui::DialogButton button); + + // Override this function if with a view which will be shown in the same + // row as the OK and CANCEL buttons but flush to the left and extending + // up to the buttons. + virtual View* GetExtraView(); + + // Returns whether the height of the extra view should be at least as tall as + // the buttons. The default (false) is to give the extra view it's preferred + // height. By returning true the height becomes + // max(extra_view preferred height, buttons preferred height). + virtual bool GetSizeExtraViewHeightToButtons(); + + // For Dialog boxes, if there is a "Cancel" button, this is called when the + // user presses the "Cancel" button or the Close button on the window or + // in the system menu, or presses the Esc key. This function should return + // true if the window can be closed after it returns, or false if it must + // remain open. + virtual bool Cancel(); + + // For Dialog boxes, this is called when the user presses the "OK" button, + // or the Enter key. Can also be called on Esc key or close button + // presses if there is no "Cancel" button. This function should return + // true if the window can be closed after it returns, or false if it must + // remain open. If |window_closing| is true, it means that this handler is + // being called because the window is being closed (e.g. by Window::Close) + // and there is no Cancel handler, so Accept is being called instead. + virtual bool Accept(bool window_closing); + virtual bool Accept(); + + // Overridden from WindowDelegate: + virtual View* GetInitiallyFocusedView() OVERRIDE; + virtual ClientView* CreateClientView(Widget* widget) OVERRIDE; + + // Called when the window has been closed. + virtual void OnClose() {} + + // A helper for accessing the DialogClientView object contained by this + // delegate's Window. + const DialogClientView* GetDialogClientView() const; + DialogClientView* GetDialogClientView(); + + protected: + // Overridden from WindowDelegate: + virtual ui::AccessibilityTypes::Role GetAccessibleWindowRole() const OVERRIDE; +}; + +// A DialogDelegate implementation that is-a View. Used to override GetWidget() +// to call View's GetWidget() for the common case where a DialogDelegate +// implementation is-a View. +class VIEWS_EXPORT DialogDelegateView : public DialogDelegate, + public View { + public: + DialogDelegateView(); + virtual ~DialogDelegateView(); + + // Overridden from DialogDelegate: + virtual Widget* GetWidget() OVERRIDE; + virtual const Widget* GetWidget() const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(DialogDelegateView); +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_DIALOG_DELEGATE_H_ diff --git a/ui/views/window/native_frame_view.cc b/ui/views/window/native_frame_view.cc new file mode 100644 index 0000000..8cb7dfd --- /dev/null +++ b/ui/views/window/native_frame_view.cc @@ -0,0 +1,74 @@ +// Copyright (c) 2011 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 "views/window/native_frame_view.h" + +#include "views/widget/native_widget.h" +#include "views/widget/widget.h" + +#if defined(OS_WIN) && !defined(USE_AURA) +#include "views/widget/native_widget_win.h" +#endif + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NativeFrameView, public: + +NativeFrameView::NativeFrameView(Widget* frame) + : NonClientFrameView(), + frame_(frame) { +} + +NativeFrameView::~NativeFrameView() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeFrameView, NonClientFrameView overrides: + +gfx::Rect NativeFrameView::GetBoundsForClientView() const { + return gfx::Rect(0, 0, width(), height()); +} + +gfx::Rect NativeFrameView::GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const { +#if defined(OS_WIN) && !defined(USE_AURA) + RECT rect = client_bounds.ToRECT(); + NativeWidgetWin* widget_win = + static_cast<NativeWidgetWin*>(frame_->native_widget()); + AdjustWindowRectEx(&rect, widget_win->window_style(), FALSE, + widget_win->window_ex_style()); + return gfx::Rect(rect); +#else + // TODO(sad): + return client_bounds; +#endif +} + +int NativeFrameView::NonClientHitTest(const gfx::Point& point) { + return frame_->client_view()->NonClientHitTest(point); +} + +void NativeFrameView::GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) { + // Nothing to do, we use the default window mask. +} + +void NativeFrameView::EnableClose(bool enable) { + // Nothing to do, handled automatically by Window. +} + +void NativeFrameView::ResetWindowControls() { + // Nothing to do. +} + +void NativeFrameView::UpdateWindowIcon() { + // Nothing to do. +} + +gfx::Size NativeFrameView::GetPreferredSize() { + return frame_->client_view()->GetPreferredSize(); +} + +} // namespace views diff --git a/ui/views/window/native_frame_view.h b/ui/views/window/native_frame_view.h new file mode 100644 index 0000000..de7f8ae --- /dev/null +++ b/ui/views/window/native_frame_view.h @@ -0,0 +1,48 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_NATIVE_FRAME_VIEW_H_ +#define UI_VIEWS_WINDOW_NATIVE_FRAME_VIEW_H_ +#pragma once + +#include "views/window/non_client_view.h" + +namespace views { + +class Widget; + +class VIEWS_EXPORT NativeFrameView : public NonClientFrameView { + public: + explicit NativeFrameView(Widget* frame); + virtual ~NativeFrameView(); + + // NonClientFrameView overrides: + 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 EnableClose(bool enable) OVERRIDE; + virtual void ResetWindowControls() OVERRIDE; + virtual void UpdateWindowIcon() OVERRIDE; + + // View overrides: + + // Returns the client size. On Windows, this is the expected behavior for + // native frames (see |NativeWidgetWin::WidgetSizeIsClientSize()|), while + // other platforms currently always return client bounds from + // |GetWindowBoundsForClientBounds()|. + virtual gfx::Size GetPreferredSize() OVERRIDE; + + private: + // Our containing frame. + Widget* frame_; + + DISALLOW_COPY_AND_ASSIGN(NativeFrameView); +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_NATIVE_FRAME_VIEW_H_ diff --git a/ui/views/window/non_client_view.cc b/ui/views/window/non_client_view.cc new file mode 100644 index 0000000..be1cdac --- /dev/null +++ b/ui/views/window/non_client_view.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2011 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 "views/window/non_client_view.h" + +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/hit_test.h" +#include "views/widget/root_view.h" +#include "views/widget/widget.h" +#include "views/window/client_view.h" + +namespace views { + +// static +const int NonClientFrameView::kFrameShadowThickness = 1; +const int NonClientFrameView::kClientEdgeThickness = 1; +const char NonClientFrameView::kViewClassName[] = + "views/window/NonClientFrameView"; + +const char NonClientView::kViewClassName[] = + "views/window/NonClientView"; + +// The frame view and the client view are always at these specific indices, +// because the RootView message dispatch sends messages to items higher in the +// z-order first and we always want the client view to have first crack at +// handling mouse messages. +static const int kFrameViewIndex = 0; +static const int kClientViewIndex = 1; + +//////////////////////////////////////////////////////////////////////////////// +// NonClientView, public: + +NonClientView::NonClientView() + : client_view_(NULL) { +} + +NonClientView::~NonClientView() { + // This value may have been reset before the window hierarchy shuts down, + // so we need to manually remove it. + RemoveChildView(frame_view_.get()); +} + +void NonClientView::SetFrameView(NonClientFrameView* frame_view) { + // See comment in header about ownership. + frame_view->set_parent_owned(false); + if (frame_view_.get()) + RemoveChildView(frame_view_.get()); + frame_view_.reset(frame_view); + if (parent()) + AddChildViewAt(frame_view_.get(), kFrameViewIndex); +} + +bool NonClientView::CanClose() { + return client_view_->CanClose(); +} + +void NonClientView::WindowClosing() { + client_view_->WidgetClosing(); +} + +void NonClientView::UpdateFrame() { + Widget* widget = GetWidget(); + SetFrameView(widget->CreateNonClientFrameView()); + widget->ThemeChanged(); + Layout(); + SchedulePaint(); + widget->UpdateFrameAfterFrameChange(); +} + +void NonClientView::SetInactiveRenderingDisabled(bool disable) { + frame_view_->SetInactiveRenderingDisabled(disable); +} + +gfx::Rect NonClientView::GetWindowBoundsForClientBounds( + const gfx::Rect client_bounds) const { + return frame_view_->GetWindowBoundsForClientBounds(client_bounds); +} + +int NonClientView::NonClientHitTest(const gfx::Point& point) { + // The NonClientFrameView is responsible for also asking the ClientView. + return frame_view_->NonClientHitTest(point); +} + +void NonClientView::GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) { + frame_view_->GetWindowMask(size, window_mask); +} + +void NonClientView::EnableClose(bool enable) { + frame_view_->EnableClose(enable); +} + +void NonClientView::ResetWindowControls() { + frame_view_->ResetWindowControls(); +} + +void NonClientView::UpdateWindowIcon() { + frame_view_->UpdateWindowIcon(); +} + +void NonClientView::LayoutFrameView() { + // First layout the NonClientFrameView, which determines the size of the + // ClientView... + frame_view_->SetBounds(0, 0, width(), height()); + + // We need to manually call Layout here because layout for the frame view can + // change independently of the bounds changing - e.g. after the initial + // display of the window the metrics of the native window controls can change, + // which does not change the bounds of the window but requires a re-layout to + // trigger a repaint. We override OnBoundsChanged() for the NonClientFrameView + // to do nothing so that SetBounds above doesn't cause Layout to be called + // twice. + frame_view_->Layout(); +} + +void NonClientView::SetAccessibleName(const string16& name) { + accessible_name_ = name; +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientView, View overrides: + +gfx::Size NonClientView::GetPreferredSize() { + // TODO(pkasting): This should probably be made to look similar to + // GetMinimumSize() below. This will require implementing GetPreferredSize() + // better in the various frame views. + gfx::Rect client_bounds(gfx::Point(), client_view_->GetPreferredSize()); + return GetWindowBoundsForClientBounds(client_bounds).size(); +} + +gfx::Size NonClientView::GetMinimumSize() { + return frame_view_->GetMinimumSize(); +} + +void NonClientView::Layout() { + LayoutFrameView(); + + // Then layout the ClientView, using those bounds. + client_view_->SetBoundsRect(frame_view_->GetBoundsForClientView()); + + // We need to manually call Layout on the ClientView as well for the same + // reason as above. + client_view_->Layout(); +} + +void NonClientView::ViewHierarchyChanged(bool is_add, View* parent, + View* child) { + // Add our two child views here as we are added to the Widget so that if we + // are subsequently resized all the parent-child relationships are + // established. + if (is_add && GetWidget() && child == this) { + AddChildViewAt(frame_view_.get(), kFrameViewIndex); + AddChildViewAt(client_view_, kClientViewIndex); + } +} + +void NonClientView::GetAccessibleState(ui::AccessibleViewState* state) { + state->role = ui::AccessibilityTypes::ROLE_WINDOW; + state->name = accessible_name_; +} + +std::string NonClientView::GetClassName() const { + return kViewClassName; +} + +views::View* NonClientView::GetEventHandlerForPoint(const gfx::Point& point) { + // Because of the z-ordering of our child views (the client view is positioned + // over the non-client frame view, if the client view ever overlaps the frame + // view visually (as it does for the browser window), then it will eat mouse + // events for the window controls. We override this method here so that we can + // detect this condition and re-route the events to the non-client frame view. + // The assumption is that the frame view's implementation of HitTest will only + // return true for area not occupied by the client view. + gfx::Point point_in_child_coords(point); + View::ConvertPointToView(this, frame_view_.get(), &point_in_child_coords); + if (frame_view_->HitTest(point_in_child_coords)) + return frame_view_->GetEventHandlerForPoint(point_in_child_coords); + + return View::GetEventHandlerForPoint(point); +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameView, public: + +void NonClientFrameView::SetInactiveRenderingDisabled(bool disable) { + // See comment in Widget::SetInactiveRenderingDisabled as to why we don't + // conditionally invoke ShouldPaintAsActiveChanged. + paint_as_active_ = disable; + ShouldPaintAsActiveChanged(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameView, View overrides: + +bool NonClientFrameView::HitTest(const gfx::Point& l) const { + // For the default case, we assume the non-client frame view never overlaps + // the client view. + return !GetWidget()->client_view()->bounds().Contains(l); +} + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameView, protected: + +int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point, + int top_resize_border_height, + int resize_border_thickness, + int top_resize_corner_height, + int resize_corner_width, + bool can_resize) { + // Tricky: In XP, native behavior is to return HTTOPLEFT and HTTOPRIGHT for + // a |resize_corner_size|-length strip of both the side and top borders, but + // only to return HTBOTTOMLEFT/HTBOTTOMRIGHT along the bottom border + corner + // (not the side border). Vista goes further and doesn't return these on any + // of the side borders. We allow callers to match either behavior. + int component; + if (point.x() < resize_border_thickness) { + if (point.y() < top_resize_corner_height) + component = HTTOPLEFT; + else if (point.y() >= (height() - resize_border_thickness)) + component = HTBOTTOMLEFT; + else + component = HTLEFT; + } else if (point.x() >= (width() - resize_border_thickness)) { + if (point.y() < top_resize_corner_height) + component = HTTOPRIGHT; + else if (point.y() >= (height() - resize_border_thickness)) + component = HTBOTTOMRIGHT; + else + component = HTRIGHT; + } else if (point.y() < top_resize_border_height) { + if (point.x() < resize_corner_width) + component = HTTOPLEFT; + else if (point.x() >= (width() - resize_corner_width)) + component = HTTOPRIGHT; + else + component = HTTOP; + } else if (point.y() >= (height() - resize_border_thickness)) { + if (point.x() < resize_corner_width) + component = HTBOTTOMLEFT; + else if (point.x() >= (width() - resize_corner_width)) + component = HTBOTTOMRIGHT; + else + component = HTBOTTOM; + } else { + return HTNOWHERE; + } + + // If the window can't be resized, there are no resize boundaries, just + // window borders. + return can_resize ? component : HTBORDER; +} + +bool NonClientFrameView::ShouldPaintAsActive() const { + return GetWidget()->IsActive() || paint_as_active_; +} + +void NonClientFrameView::ShouldPaintAsActiveChanged() { + if (!paint_as_active_) + SchedulePaint(); +} + +void NonClientFrameView::GetAccessibleState(ui::AccessibleViewState* state) { + state->role = ui::AccessibilityTypes::ROLE_WINDOW; +} + +std::string NonClientFrameView::GetClassName() const { + return kViewClassName; +} + +void NonClientFrameView::OnBoundsChanged(const gfx::Rect& previous_bounds) { + // Overridden to do nothing. The NonClientView manually calls Layout on the + // FrameView when it is itself laid out, see comment in NonClientView::Layout. +} + +} // namespace views diff --git a/ui/views/window/non_client_view.h b/ui/views/window/non_client_view.h new file mode 100644 index 0000000..db2ab9b --- /dev/null +++ b/ui/views/window/non_client_view.h @@ -0,0 +1,240 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_NON_CLIENT_VIEW_H_ +#define UI_VIEWS_WINDOW_NON_CLIENT_VIEW_H_ +#pragma once + +#include "views/view.h" +#include "views/window/client_view.h" + +namespace gfx { +class Path; +} + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NonClientFrameView +// +// An object that subclasses NonClientFrameView is a View that renders and +// responds to events within the frame portions of the non-client area of a +// window. This view does _not_ contain the ClientView, but rather is a sibling +// of it. +class VIEWS_EXPORT NonClientFrameView : public View { + public: + // Internal class name. + static const char kViewClassName[]; + // Various edges of the frame border have a 1 px shadow along their edges; in + // a few cases we shift elements based on this amount for visual appeal. + static const int kFrameShadowThickness; + // In restored mode, we draw a 1 px edge around the content area inside the + // frame border. + static const int kClientEdgeThickness; + + // Sets whether the window should be rendered as active regardless of the + // actual active state. Used when bubbles become active to make their parent + // appear active. A value of true makes the window render as active always, + // false gives normal behavior. + void SetInactiveRenderingDisabled(bool disable); + + // Returns the bounds (in this View's parent's coordinates) that the client + // view should be laid out within. + virtual gfx::Rect GetBoundsForClientView() const = 0; + + virtual gfx::Rect GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const = 0; + + // This function must ask the ClientView to do a hittest. We don't do this in + // the parent NonClientView because that makes it more difficult to calculate + // hittests for regions that are partially obscured by the ClientView, e.g. + // HTSYSMENU. + virtual int NonClientHitTest(const gfx::Point& point) = 0; + virtual void GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) = 0; + virtual void EnableClose(bool enable) = 0; + virtual void ResetWindowControls() = 0; + virtual void UpdateWindowIcon() = 0; + + // Overridden from View: + virtual bool HitTest(const gfx::Point& l) const OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual std::string GetClassName() const OVERRIDE; + + protected: + virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; + + NonClientFrameView() : paint_as_active_(false) {} + + // Helper for non-client view implementations to determine which area of the + // window border the specified |point| falls within. The other parameters are + // the size of the sizing edges, and whether or not the window can be + // resized. + int GetHTComponentForFrame(const gfx::Point& point, + int top_resize_border_height, + int resize_border_thickness, + int top_resize_corner_height, + int resize_corner_width, + bool can_resize); + + // Used to determine if the frame should be painted as active. Keyed off the + // window's actual active state and the override, see + // SetInactiveRenderingDisabled() above. + bool ShouldPaintAsActive() const; + + // Invoked from SetInactiveRenderingDisabled(). This implementation invokes + // SchedulesPaint as necessary. + virtual void ShouldPaintAsActiveChanged(); + + private: + // True when the non-client view should always be rendered as if the window + // were active, regardless of whether or not the top level window actually + // is active. + bool paint_as_active_; +}; + +//////////////////////////////////////////////////////////////////////////////// +// NonClientView +// +// The NonClientView is the logical root of all Views contained within a +// Window, except for the RootView which is its parent and of which it is the +// sole child. The NonClientView has two children, the NonClientFrameView which +// is responsible for painting and responding to events from the non-client +// portions of the window, and the ClientView, which is responsible for the +// same for the client area of the window: +// +// +- views::Window ------------------------------------+ +// | +- views::RootView ------------------------------+ | +// | | +- views::NonClientView ---------------------+ | | +// | | | +- views::NonClientFrameView subclas ---+ | | | +// | | | | | | | | +// | | | | << all painting and event receiving >> | | | | +// | | | | << of the non-client areas of a >> | | | | +// | | | | << views::Window. >> | | | | +// | | | | | | | | +// | | | +----------------------------------------+ | | | +// | | | +- views::ClientView or subclass --------+ | | | +// | | | | | | | | +// | | | | << all painting and event receiving >> | | | | +// | | | | << of the client areas of a >> | | | | +// | | | | << views::Window. >> | | | | +// | | | | | | | | +// | | | +----------------------------------------+ | | | +// | | +--------------------------------------------+ | | +// | +------------------------------------------------+ | +// +----------------------------------------------------+ +// +// The NonClientFrameView and ClientView are siblings because due to theme +// changes the NonClientFrameView may be replaced with different +// implementations (e.g. during the switch from DWM/Aero-Glass to Vista Basic/ +// Classic rendering). +// +class VIEWS_EXPORT NonClientView : public View { + public: + // Internal class name. + static const char kViewClassName[]; + + NonClientView(); + virtual ~NonClientView(); + + // Returns the current NonClientFrameView instance, or NULL if + // it does not exist. + NonClientFrameView* frame_view() const { return frame_view_.get(); } + + // Replaces the current NonClientFrameView (if any) with the specified one. + void SetFrameView(NonClientFrameView* frame_view); + + // Returns true if the ClientView determines that the containing window can be + // closed, false otherwise. + bool CanClose(); + + // Called by the containing Window when it is closed. + void WindowClosing(); + + // Changes the frame from native to custom depending on the value of + // |use_native_frame|. + void UpdateFrame(); + + // Prevents the window from being rendered as deactivated when |disable| is + // true, until called with |disable| false. Used when a sub-window is to be + // shown that shouldn't visually de-activate the window. + // Subclasses can override this to perform additional actions when this value + // changes. + void SetInactiveRenderingDisabled(bool disable); + + // Returns the bounds of the window required to display the content area at + // the specified bounds. + gfx::Rect GetWindowBoundsForClientBounds(const gfx::Rect client_bounds) const; + + // Determines the windows HT* code when the mouse cursor is at the + // specified point, in window coordinates. + int NonClientHitTest(const gfx::Point& point); + + // Returns a mask to be used to clip the top level window for the given + // size. This is used to create the non-rectangular window shape. + void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask); + + // Toggles the enable state for the Close button (and the Close menu item in + // the system menu). + void EnableClose(bool enable); + + // Tells the window controls as rendered by the NonClientView to reset + // themselves to a normal state. This happens in situations where the + // containing window does not receive a normal sequences of messages that + // would lead to the controls returning to this normal state naturally, e.g. + // when the window is maximized, minimized or restored. + void ResetWindowControls(); + + // Tells the NonClientView to invalidate the NonClientFrameView's window icon. + void UpdateWindowIcon(); + + // Get/Set client_view property. + ClientView* client_view() const { return client_view_; } + void set_client_view(ClientView* client_view) { + client_view_ = client_view; + } + + // Layout just the frame view. This is necessary on Windows when non-client + // metrics such as the position of the window controls changes independently + // of a window resize message. + void LayoutFrameView(); + + // Set the accessible name of this view. + void SetAccessibleName(const string16& name); + + // NonClientView, View overrides: + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::Size GetMinimumSize() OVERRIDE; + virtual void Layout() OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + virtual std::string GetClassName() const OVERRIDE; + + virtual views::View* GetEventHandlerForPoint(const gfx::Point& point) + OVERRIDE; + + protected: + // NonClientView, View overrides: + virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child) + OVERRIDE; + + private: + // A ClientView object or subclass, responsible for sizing the contents view + // of the window, hit testing and perhaps other tasks depending on the + // implementation. + ClientView* client_view_; + + // The NonClientFrameView that renders the non-client portions of the window. + // This object is not owned by the view hierarchy because it can be replaced + // dynamically as the system settings change. + scoped_ptr<NonClientFrameView> frame_view_; + + // The accessible name of this view. + string16 accessible_name_; + + DISALLOW_COPY_AND_ASSIGN(NonClientView); +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_NON_CLIENT_VIEW_H_ diff --git a/ui/views/window/window_resources.h b/ui/views/window/window_resources.h new file mode 100644 index 0000000..14afc43 --- /dev/null +++ b/ui/views/window/window_resources.h @@ -0,0 +1,32 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_WINDOW_RESOURCES_H_ +#define UI_VIEWS_WINDOW_WINDOW_RESOURCES_H_ +#pragma once + +class SkBitmap; + +namespace views { + +typedef int FramePartBitmap; + +/////////////////////////////////////////////////////////////////////////////// +// WindowResources +// +// An interface implemented by an object providing bitmaps to render the +// contents of a window frame. The Window may swap in different +// implementations of this interface to render different modes. The definition +// of FramePartBitmap depends on the implementation. +// +class WindowResources { + public: + virtual ~WindowResources() {} + + virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_WINDOW_WINDOW_RESOURCES_H_ diff --git a/ui/views/window/window_shape.cc b/ui/views/window/window_shape.cc new file mode 100644 index 0000000..869ec21 --- /dev/null +++ b/ui/views/window/window_shape.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2011 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 "views/window/window_shape.h" + +#include "ui/gfx/path.h" +#include "ui/gfx/size.h" + +namespace views { + +void GetDefaultWindowMask(const gfx::Size &size, gfx::Path *window_mask) { + // Redefine the window visible region for the new size. + window_mask->moveTo(0, 3); + window_mask->lineTo(1, 2); + window_mask->lineTo(1, 1); + window_mask->lineTo(2, 1); + window_mask->lineTo(3, 0); + + window_mask->lineTo(SkIntToScalar(size.width() - 3), 0); + window_mask->lineTo(SkIntToScalar(size.width() - 2), 1); + window_mask->lineTo(SkIntToScalar(size.width() - 1), 1); + window_mask->lineTo(SkIntToScalar(size.width() - 1), 2); + window_mask->lineTo(SkIntToScalar(size.width()), 3); + + window_mask->lineTo(SkIntToScalar(size.width()), + SkIntToScalar(size.height() - 3)); + window_mask->lineTo(SkIntToScalar(size.width() - 1), + SkIntToScalar(size.height() - 3)); + window_mask->lineTo(SkIntToScalar(size.width() - 1), + SkIntToScalar(size.height() - 1)); + window_mask->lineTo(SkIntToScalar(size.width() - 3), + SkIntToScalar(size.height() - 2)); + window_mask->lineTo(SkIntToScalar(size.width() - 3), + SkIntToScalar(size.height())); + + window_mask->lineTo(3, SkIntToScalar(size.height())); + window_mask->lineTo(2, SkIntToScalar(size.height() - 2)); + window_mask->lineTo(1, SkIntToScalar(size.height() - 1)); + window_mask->lineTo(1, SkIntToScalar(size.height() - 3)); + window_mask->lineTo(0, SkIntToScalar(size.height() - 3)); + + window_mask->close(); +} + +} // namespace views diff --git a/ui/views/window/window_shape.h b/ui/views/window/window_shape.h new file mode 100644 index 0000000..6db3add --- /dev/null +++ b/ui/views/window/window_shape.h @@ -0,0 +1,25 @@ +// Copyright (c) 2011 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 UI_VIEWS_WINDOW_WINDOW_SHAPE_H_ +#define UI_VIEWS_WINDOW_WINDOW_SHAPE_H_ +#pragma once + +#include "views/views_export.h" + +namespace gfx { +class Size; +class Path; +} + +namespace views { + +// Sets the window mask to a style that most likely matches +// ui/resources/window_* +VIEWS_EXPORT void GetDefaultWindowMask(const gfx::Size& size, + gfx::Path* window_mask); + +} // namespace views + +#endif // UI_VIEWS_WINDOW_WINDOW_SHAPE_H_ |