diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-27 03:01:54 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-27 03:01:54 +0000 |
commit | 8eeaf57c9f1ac55bdc68b8d73fd12a16c5e4c9b9 (patch) | |
tree | ad5e08613069b9480934fbd5e0492a6b273ec1c4 /chrome/views | |
parent | dcbffbacd7e233de6a13876ff99edf323c9bcfa6 (diff) | |
download | chromium_src-8eeaf57c9f1ac55bdc68b8d73fd12a16c5e4c9b9.zip chromium_src-8eeaf57c9f1ac55bdc68b8d73fd12a16c5e4c9b9.tar.gz chromium_src-8eeaf57c9f1ac55bdc68b8d73fd12a16c5e4c9b9.tar.bz2 |
Move DefaultNonClientView out into its own cc/h file.
Review URL: http://codereview.chromium.org/28246
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10577 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/views')
-rw-r--r-- | chrome/views/custom_frame_window.cc | 797 | ||||
-rw-r--r-- | chrome/views/default_non_client_view.cc | 704 | ||||
-rw-r--r-- | chrome/views/default_non_client_view.h | 125 | ||||
-rw-r--r-- | chrome/views/views.vcproj | 8 |
4 files changed, 839 insertions, 795 deletions
diff --git a/chrome/views/custom_frame_window.cc b/chrome/views/custom_frame_window.cc index 110750f..d615154 100644 --- a/chrome/views/custom_frame_window.cc +++ b/chrome/views/custom_frame_window.cc @@ -6,22 +6,15 @@ #include "base/gfx/point.h" #include "base/gfx/size.h" -#include "base/win_util.h" -#include "chrome/common/gfx/path.h" #include "chrome/common/gfx/chrome_canvas.h" -#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/gfx/path.h" #include "chrome/common/l10n_util.h" -#include "chrome/common/resource_bundle.h" #include "chrome/common/win_util.h" -#include "chrome/views/button.h" #include "chrome/views/client_view.h" -#include "chrome/views/native_button.h" -#include "chrome/views/non_client_view.h" +#include "chrome/views/default_non_client_view.h" #include "chrome/views/root_view.h" #include "chrome/views/window_delegate.h" -#include "chrome/views/window_resources.h" #include "grit/generated_resources.h" -#include "grit/theme_resources.h" namespace views { @@ -67,792 +60,6 @@ class CustomFrameWindow::ScopedRedrawLock { HCURSOR CustomFrameWindow::resize_cursors_[6]; -// An enumeration of bitmap resources used by this window. -enum { - FRAME_PART_BITMAP_FIRST = 0, // Must be first. - - // Window Controls. - FRAME_CLOSE_BUTTON_ICON, - FRAME_CLOSE_BUTTON_ICON_H, - FRAME_CLOSE_BUTTON_ICON_P, - FRAME_CLOSE_BUTTON_ICON_SA, - FRAME_CLOSE_BUTTON_ICON_SA_H, - FRAME_CLOSE_BUTTON_ICON_SA_P, - FRAME_RESTORE_BUTTON_ICON, - FRAME_RESTORE_BUTTON_ICON_H, - FRAME_RESTORE_BUTTON_ICON_P, - FRAME_MAXIMIZE_BUTTON_ICON, - FRAME_MAXIMIZE_BUTTON_ICON_H, - FRAME_MAXIMIZE_BUTTON_ICON_P, - FRAME_MINIMIZE_BUTTON_ICON, - FRAME_MINIMIZE_BUTTON_ICON_H, - FRAME_MINIMIZE_BUTTON_ICON_P, - - // Window Frame Border. - FRAME_BOTTOM_EDGE, - FRAME_BOTTOM_LEFT_CORNER, - FRAME_BOTTOM_RIGHT_CORNER, - FRAME_LEFT_EDGE, - FRAME_RIGHT_EDGE, - FRAME_TOP_EDGE, - FRAME_TOP_LEFT_CORNER, - FRAME_TOP_RIGHT_CORNER, - - // Client Edge Border. - FRAME_CLIENT_EDGE_TOP_LEFT, - FRAME_CLIENT_EDGE_TOP, - FRAME_CLIENT_EDGE_TOP_RIGHT, - FRAME_CLIENT_EDGE_RIGHT, - FRAME_CLIENT_EDGE_BOTTOM_RIGHT, - FRAME_CLIENT_EDGE_BOTTOM, - FRAME_CLIENT_EDGE_BOTTOM_LEFT, - FRAME_CLIENT_EDGE_LEFT, - - FRAME_PART_BITMAP_COUNT // Must be last. -}; - -class ActiveWindowResources : public WindowResources { - public: - ActiveWindowResources() { - InitClass(); - } - virtual ~ActiveWindowResources() { - } - - // WindowResources implementation: - virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { - return standard_frame_bitmaps_[part]; - } - - private: - static void InitClass() { - static bool initialized = false; - if (!initialized) { - static const int kFramePartBitmapIds[] = { - 0, - IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, - IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, - IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, - IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, - IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, - IDR_WINDOW_BOTTOM_CENTER, IDR_WINDOW_BOTTOM_LEFT_CORNER, - IDR_WINDOW_BOTTOM_RIGHT_CORNER, IDR_WINDOW_LEFT_SIDE, - IDR_WINDOW_RIGHT_SIDE, IDR_WINDOW_TOP_CENTER, - IDR_WINDOW_TOP_LEFT_CORNER, IDR_WINDOW_TOP_RIGHT_CORNER, - IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, - IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, - IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, - IDR_CONTENT_LEFT_SIDE, - 0 - }; - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { - int id = kFramePartBitmapIds[i]; - if (id != 0) - standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); - } - initialized = true; - } - } - - static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; - static ChromeFont title_font_; - - DISALLOW_EVIL_CONSTRUCTORS(ActiveWindowResources); -}; - -class InactiveWindowResources : public WindowResources { - public: - InactiveWindowResources() { - InitClass(); - } - virtual ~InactiveWindowResources() { - } - - // WindowResources implementation: - virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { - return standard_frame_bitmaps_[part]; - } - - private: - static void InitClass() { - static bool initialized = false; - if (!initialized) { - static const int kFramePartBitmapIds[] = { - 0, - IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, - IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, - IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, - IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, - IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, - IDR_DEWINDOW_BOTTOM_CENTER, IDR_DEWINDOW_BOTTOM_LEFT_CORNER, - IDR_DEWINDOW_BOTTOM_RIGHT_CORNER, IDR_DEWINDOW_LEFT_SIDE, - IDR_DEWINDOW_RIGHT_SIDE, IDR_DEWINDOW_TOP_CENTER, - IDR_DEWINDOW_TOP_LEFT_CORNER, IDR_DEWINDOW_TOP_RIGHT_CORNER, - IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, - IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, - IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, - IDR_CONTENT_LEFT_SIDE, - 0 - }; - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { - int id = kFramePartBitmapIds[i]; - if (id != 0) - standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); - } - initialized = true; - } - } - - static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; - - DISALLOW_EVIL_CONSTRUCTORS(InactiveWindowResources); -}; - -// static -SkBitmap* ActiveWindowResources::standard_frame_bitmaps_[]; -SkBitmap* InactiveWindowResources::standard_frame_bitmaps_[]; - -/////////////////////////////////////////////////////////////////////////////// -// -// DefaultNonClientView -// -// A ChromeView that provides the "frame" for CustomFrameWindows. This means -// rendering the non-standard window caption, border, and controls. -// -//////////////////////////////////////////////////////////////////////////////// -class DefaultNonClientView : public NonClientView, - public BaseButton::ButtonListener { - public: - explicit DefaultNonClientView(CustomFrameWindow* container); - virtual ~DefaultNonClientView(); - - // Overridden from views::NonClientView: - virtual gfx::Rect CalculateClientAreaBounds(int width, int height) const; - virtual gfx::Size CalculateWindowSizeForClientSize(int width, - int height) const; - virtual gfx::Point GetSystemMenuPoint() const; - virtual int NonClientHitTest(const gfx::Point& point); - virtual void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask); - virtual void EnableClose(bool enable); - virtual void ResetWindowControls(); - - // View overrides: - virtual void Paint(ChromeCanvas* canvas); - virtual void Layout(); - virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child); - - // BaseButton::ButtonListener implementation: - virtual void ButtonPressed(BaseButton* sender); - - 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; - - // A bottom border, and, in restored mode, a client edge are drawn at the - // bottom of the titlebar. This returns the total height drawn. - int BottomEdgeThicknessWithinNonClientHeight() const; - - // Calculates multiple values related to title layout. Returns the height of - // the entire titlebar including any connected client edge. - int TitleCoordinates(int* title_top_spacing, - int* title_thickness) const; - - // Paint various sub-components of this view. - void PaintRestoredFrameBorder(ChromeCanvas* canvas); - void PaintMaximizedFrameBorder(ChromeCanvas* canvas); - void PaintTitleBar(ChromeCanvas* canvas); - void PaintRestoredClientEdge(ChromeCanvas* canvas); - - // Layout various sub-components of this view. - void LayoutWindowControls(); - void LayoutTitleBar(); - void LayoutClientView(); - - // Returns the resource collection to be used when rendering the window. - WindowResources* resources() const { - return container_->is_active() || paint_as_active() ? active_resources_ - : inactive_resources_; - } - - // The View that provides the background for the window, and optionally - // dialog buttons. Note: the non-client view does _not_ own this view, the - // container does. - ClientView* client_view_; - - // The layout rect of the title, if visible. - gfx::Rect title_bounds_; - - // Window controls. - Button* close_button_; - Button* restore_button_; - Button* maximize_button_; - Button* minimize_button_; - Button* system_menu_button_; // Uses the window icon if visible. - bool should_show_minmax_buttons_; - - // The window that owns this view. - CustomFrameWindow* container_; - - // Initialize various static resources. - static void InitClass(); - static WindowResources* active_resources_; - static WindowResources* inactive_resources_; - static ChromeFont title_font_; - - DISALLOW_EVIL_CONSTRUCTORS(DefaultNonClientView); -}; - -// static -WindowResources* DefaultNonClientView::active_resources_ = NULL; -WindowResources* DefaultNonClientView::inactive_resources_ = NULL; -ChromeFont DefaultNonClientView::title_font_; - -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 to less than 18 px tall, plus the height of the -// frame border and any bottom edge. -const int kTitlebarMinimumHeight = 18; -// The icon is inset 2 px from the left frame border. -const int kIconLeftSpacing = 2; -// The icon takes up 16/25th of the available titlebar height. (This is -// expressed as two ints to avoid precision losses leading to off-by-one pixel -// errors.) -const int kIconHeightFractionNumerator = 16; -const int kIconHeightFractionDenominator = 25; -// The icon never shrinks below 16 px on a side. -const int kIconMinimumSize = 16; -// Because our frame border has a different "3D look" than Windows', with a less -// cluttered top edge, we need to shift the icon up by 1 px in restored mode so -// it looks more centered. -const int kIconRestoredAdjust = 1; -// There is a 4 px gap between the icon and the title text. -const int kIconTitleSpacing = 4; -// The title text starts 2 px below the bottom of the top frame border. -const int kTitleTopSpacing = 2; -// There is a 5 px gap between the title text and the caption buttons. -const int kTitleCaptionSpacing = 5; -// The caption buttons are always drawn 1 px down from the visible top of the -// window (the true top in restored mode, or the top of the screen in maximized -// mode). -const int kCaptionTopSpacing = 1; -} - -/////////////////////////////////////////////////////////////////////////////// -// DefaultNonClientView, public: - -DefaultNonClientView::DefaultNonClientView( - CustomFrameWindow* container) - : NonClientView(), - client_view_(NULL), - close_button_(new Button), - restore_button_(new Button), - maximize_button_(new Button), - minimize_button_(new Button), - system_menu_button_(new Button), - should_show_minmax_buttons_(false), - container_(container) { - InitClass(); - WindowResources* resources = active_resources_; - - // Close button images will be set in LayoutWindowControls(). - close_button_->SetListener(this, -1); - AddChildView(close_button_); - - restore_button_->SetImage(Button::BS_NORMAL, - resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON)); - restore_button_->SetImage(Button::BS_HOT, - resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_H)); - restore_button_->SetImage(Button::BS_PUSHED, - resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_P)); - restore_button_->SetListener(this, -1); - AddChildView(restore_button_); - - maximize_button_->SetImage(Button::BS_NORMAL, - resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON)); - maximize_button_->SetImage(Button::BS_HOT, - resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_H)); - maximize_button_->SetImage(Button::BS_PUSHED, - resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_P)); - maximize_button_->SetListener(this, -1); - AddChildView(maximize_button_); - - minimize_button_->SetImage(Button::BS_NORMAL, - resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON)); - minimize_button_->SetImage(Button::BS_HOT, - resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_H)); - minimize_button_->SetImage(Button::BS_PUSHED, - resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_P)); - minimize_button_->SetListener(this, -1); - AddChildView(minimize_button_); - - should_show_minmax_buttons_ = container->window_delegate()->CanMaximize(); - - AddChildView(system_menu_button_); -} - -DefaultNonClientView::~DefaultNonClientView() { -} - -/////////////////////////////////////////////////////////////////////////////// -// DefaultNonClientView, CustomFrameWindow::NonClientView implementation: - -gfx::Rect DefaultNonClientView::CalculateClientAreaBounds(int width, - int height) const { - int top_height = NonClientTopBorderHeight(); - int border_thickness = NonClientBorderThickness(); - return gfx::Rect(border_thickness, top_height, - std::max(0, width - (2 * border_thickness)), - std::max(0, height - top_height - border_thickness)); -} - -gfx::Size DefaultNonClientView::CalculateWindowSizeForClientSize( - int width, - int height) const { - int border_thickness = NonClientBorderThickness(); - return gfx::Size(width + (2 * border_thickness), - height + NonClientTopBorderHeight() + border_thickness); -} - -gfx::Point DefaultNonClientView::GetSystemMenuPoint() const { - gfx::Point system_menu_point(FrameBorderThickness(), - NonClientTopBorderHeight() - BottomEdgeThicknessWithinNonClientHeight()); - ConvertPointToScreen(this, &system_menu_point); - return system_menu_point; -} - -int DefaultNonClientView::NonClientHitTest(const gfx::Point& point) { - if (!bounds().Contains(point)) - return HTNOWHERE; - - int frame_component = container_->client_view()->NonClientHitTest(point); - if (frame_component != HTNOWHERE) - return frame_component; - - // Then see if the point is within any of the window controls. - if (close_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) - return HTCLOSE; - if (restore_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( - point)) - return HTMAXBUTTON; - if (maximize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( - point)) - return HTMAXBUTTON; - if (minimize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( - point)) - return HTMINBUTTON; - if (system_menu_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( - point)) - return HTSYSMENU; - - int window_component = GetHTComponentForFrame(point, FrameBorderThickness(), - NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, - container_->window_delegate()->CanResize()); - // Fall back to the caption if no other component matches. - return (window_component == HTNOWHERE) ? HTCAPTION : window_component; -} - -void DefaultNonClientView::GetWindowMask(const gfx::Size& size, - gfx::Path* window_mask) { - DCHECK(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())); - window_mask->lineTo(0, SkIntToScalar(size.height())); - window_mask->close(); -} - -void DefaultNonClientView::EnableClose(bool enable) { - close_button_->SetEnabled(enable); -} - -void DefaultNonClientView::ResetWindowControls() { - restore_button_->SetState(Button::BS_NORMAL); - minimize_button_->SetState(Button::BS_NORMAL); - maximize_button_->SetState(Button::BS_NORMAL); - // The close button isn't affected by this constraint. -} - -/////////////////////////////////////////////////////////////////////////////// -// DefaultNonClientView, View overrides: - -void DefaultNonClientView::Paint(ChromeCanvas* canvas) { - if (container_->IsMaximized()) - PaintMaximizedFrameBorder(canvas); - else - PaintRestoredFrameBorder(canvas); - PaintTitleBar(canvas); - if (!container_->IsMaximized()) - PaintRestoredClientEdge(canvas); -} - -void DefaultNonClientView::Layout() { - LayoutWindowControls(); - LayoutTitleBar(); - LayoutClientView(); -} - -void DefaultNonClientView::ViewHierarchyChanged(bool is_add, - View* parent, - View* child) { - // Add our Client View 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) - AddChildView(container_->client_view()); -} - -/////////////////////////////////////////////////////////////////////////////// -// DefaultNonClientView, BaseButton::ButtonListener implementation: - -void DefaultNonClientView::ButtonPressed(BaseButton* sender) { - if (sender == close_button_) - container_->ExecuteSystemMenuCommand(SC_CLOSE); - else if (sender == minimize_button_) - container_->ExecuteSystemMenuCommand(SC_MINIMIZE); - else if (sender == maximize_button_) - container_->ExecuteSystemMenuCommand(SC_MAXIMIZE); - else if (sender == restore_button_) - container_->ExecuteSystemMenuCommand(SC_RESTORE); -} - -/////////////////////////////////////////////////////////////////////////////// -// DefaultNonClientView, private: - -int DefaultNonClientView::FrameBorderThickness() const { - return container_->IsMaximized() ? - GetSystemMetrics(SM_CXSIZEFRAME) : kFrameBorderThickness; -} - -int DefaultNonClientView::NonClientBorderThickness() const { - // In maximized mode, we don't show a client edge. - return FrameBorderThickness() + - (container_->IsMaximized() ? 0 : kClientEdgeThickness); -} - -int DefaultNonClientView::NonClientTopBorderHeight() const { - int title_top_spacing, title_thickness; - return TitleCoordinates(&title_top_spacing, &title_thickness); -} - -int DefaultNonClientView::BottomEdgeThicknessWithinNonClientHeight() const { - return kFrameShadowThickness + - (container_->IsMaximized() ? 0 : kClientEdgeThickness); -} - -int DefaultNonClientView::TitleCoordinates(int* title_top_spacing, - int* title_thickness) const { - int frame_thickness = FrameBorderThickness(); - int min_titlebar_height = kTitlebarMinimumHeight + frame_thickness; - *title_top_spacing = frame_thickness + kTitleTopSpacing; - // The bottom spacing should be the same apparent height as the top spacing. - // Because the actual top spacing height varies based on the system border - // thickness, we calculate this based on the restored top spacing and then - // adjust for maximized mode. We also don't include the frame shadow here, - // since while it's part of the bottom spacing it will be added in at the end. - int title_bottom_spacing = - kFrameBorderThickness + kTitleTopSpacing - kFrameShadowThickness; - if (container_->IsMaximized()) { - // When we maximize, the top border appears to be chopped off; shift the - // title down to stay centered within the remaining space. - int title_adjust = (kFrameBorderThickness / 2); - *title_top_spacing += title_adjust; - title_bottom_spacing -= title_adjust; - } - *title_thickness = std::max(title_font_.height(), - min_titlebar_height - *title_top_spacing - title_bottom_spacing); - return *title_top_spacing + *title_thickness + title_bottom_spacing + - BottomEdgeThicknessWithinNonClientHeight(); -} - -void DefaultNonClientView::PaintRestoredFrameBorder(ChromeCanvas* canvas) { - SkBitmap* top_left_corner = resources()->GetPartBitmap(FRAME_TOP_LEFT_CORNER); - SkBitmap* top_right_corner = - resources()->GetPartBitmap(FRAME_TOP_RIGHT_CORNER); - SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); - SkBitmap* right_edge = resources()->GetPartBitmap(FRAME_RIGHT_EDGE); - SkBitmap* left_edge = resources()->GetPartBitmap(FRAME_LEFT_EDGE); - SkBitmap* bottom_left_corner = - resources()->GetPartBitmap(FRAME_BOTTOM_LEFT_CORNER); - SkBitmap* bottom_right_corner = - resources()->GetPartBitmap(FRAME_BOTTOM_RIGHT_CORNER); - SkBitmap* bottom_edge = resources()->GetPartBitmap(FRAME_BOTTOM_EDGE); - - // 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 DefaultNonClientView::PaintMaximizedFrameBorder( - ChromeCanvas* canvas) { - SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); - canvas->TileImageInt(*top_edge, 0, FrameBorderThickness(), width(), - top_edge->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 = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); - int edge_height = titlebar_bottom->height() - kClientEdgeThickness; - canvas->TileImageInt(*titlebar_bottom, 0, - container_->client_view()->y() - edge_height, width(), edge_height); -} - -void DefaultNonClientView::PaintTitleBar(ChromeCanvas* canvas) { - WindowDelegate* d = container_->window_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, - MirroredLeftPointForRect(title_bounds_), title_bounds_.y(), - title_bounds_.width(), title_bounds_.height()); -} - -void DefaultNonClientView::PaintRestoredClientEdge(ChromeCanvas* canvas) { - gfx::Rect client_area_bounds = container_->client_view()->bounds(); - int client_area_top = client_area_bounds.y(); - - SkBitmap* top_left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT); - SkBitmap* top = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); - SkBitmap* top_right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_RIGHT); - SkBitmap* right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_RIGHT); - SkBitmap* bottom_right = - resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_RIGHT); - SkBitmap* bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM); - SkBitmap* bottom_left = - resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_LEFT); - SkBitmap* left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_LEFT); - - // Top. - // This next calculation is necessary because the top center bitmap is shorter - // than the top left and right bitmaps. We need their top edges to line up, - // and we need the left and right edges to start below the corners' bottoms. - int top_edge_y = client_area_top - top->height(); - client_area_top = top_edge_y + top_left->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); -} - -void DefaultNonClientView::LayoutWindowControls() { - close_button_->SetImageAlignment(Button::ALIGN_LEFT, Button::ALIGN_BOTTOM); - // 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. - bool is_maximized = container_->IsMaximized(); - int frame_thickness = FrameBorderThickness(); - int caption_y = is_maximized ? frame_thickness : kCaptionTopSpacing; - int top_extra_height = is_maximized ? kCaptionTopSpacing : 0; - // 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; - int right_spacing = is_maximized ? - (GetSystemMetrics(SM_CXSIZEFRAME) + right_extra_width) : frame_thickness; - gfx::Size close_button_size = close_button_->GetPreferredSize(); - close_button_->SetBounds(width() - close_button_size.width() - right_spacing, - caption_y, - close_button_size.width() + right_extra_width, - close_button_size.height() + top_extra_height); - - // When the window is restored, we show a maximized button; otherwise, we show - // a restore button. - bool is_restored = !is_maximized && !container_->IsMinimized(); - views::Button* invisible_button = is_restored ? - restore_button_ : maximize_button_; - invisible_button->SetVisible(false); - - views::Button* 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(Button::ALIGN_LEFT, Button::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() + top_extra_height); - - minimize_button_->SetVisible(true); - minimize_button_->SetImageAlignment(Button::ALIGN_LEFT, - Button::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() + top_extra_height); - - normal_part = FRAME_CLOSE_BUTTON_ICON; - hot_part = FRAME_CLOSE_BUTTON_ICON_H; - pushed_part = FRAME_CLOSE_BUTTON_ICON_P; - } else { - visible_button->SetVisible(false); - minimize_button_->SetVisible(false); - - normal_part = FRAME_CLOSE_BUTTON_ICON_SA; - hot_part = FRAME_CLOSE_BUTTON_ICON_SA_H; - pushed_part = FRAME_CLOSE_BUTTON_ICON_SA_P; - } - - close_button_->SetImage(Button::BS_NORMAL, - active_resources_->GetPartBitmap(normal_part)); - close_button_->SetImage(Button::BS_HOT, - active_resources_->GetPartBitmap(hot_part)); - close_button_->SetImage(Button::BS_PUSHED, - active_resources_->GetPartBitmap(pushed_part)); -} - -void DefaultNonClientView::LayoutTitleBar() { - // Always lay out the icon, even when it's not present, so we can lay out the - // window title based on its position. - int frame_thickness = FrameBorderThickness(); - int icon_x = frame_thickness + kIconLeftSpacing; - - // The usable height of the titlebar area is the total height minus the top - // resize border and any edge area we draw at its bottom. - int title_top_spacing, title_thickness; - int top_height = TitleCoordinates(&title_top_spacing, &title_thickness); - int available_height = top_height - frame_thickness - - BottomEdgeThicknessWithinNonClientHeight(); - - // The icon takes up a constant fraction of the available height, down to a - // minimum size, and is always an even number of pixels on a side (presumably - // to make scaled icons look better). It's centered within the usable height. - int icon_size = std::max((available_height * kIconHeightFractionNumerator / - kIconHeightFractionDenominator) / 2 * 2, kIconMinimumSize); - int icon_y = ((available_height - icon_size) / 2) + frame_thickness; - - // Hack: 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 we come up to compensate. - if (!container_->IsMaximized()) - icon_y -= kIconRestoredAdjust; - - views::WindowDelegate* d = container_->window_delegate(); - if (!d->ShouldShowWindowIcon()) - icon_size = 0; - system_menu_button_->SetBounds(icon_x, icon_y, icon_size, icon_size); - - // Size the title. - int icon_right = icon_x + icon_size; - int title_x = - icon_right + (d->ShouldShowWindowIcon() ? kIconTitleSpacing : 0); - int title_right = (should_show_minmax_buttons_ ? - minimize_button_->x() : close_button_->x()) - kTitleCaptionSpacing; - title_bounds_.SetRect(title_x, - title_top_spacing + ((title_thickness - title_font_.height()) / 2), - std::max(0, title_right - title_x), title_font_.height()); -} - -void DefaultNonClientView::LayoutClientView() { - container_->client_view()->SetBounds(CalculateClientAreaBounds(width(), - height())); -} - -// static -void DefaultNonClientView::InitClass() { - static bool initialized = false; - if (!initialized) { - active_resources_ = new ActiveWindowResources; - inactive_resources_ = new InactiveWindowResources; - - title_font_ = win_util::GetWindowTitleFont(); - - initialized = true; - } -} - /////////////////////////////////////////////////////////////////////////////// // CustomFrameWindow, public: diff --git a/chrome/views/default_non_client_view.cc b/chrome/views/default_non_client_view.cc new file mode 100644 index 0000000..dc5010c --- /dev/null +++ b/chrome/views/default_non_client_view.cc @@ -0,0 +1,704 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/views/default_non_client_view.h" + +#include "base/win_util.h" +#include "chrome/common/gfx/path.h" +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/win_util.h" +#include "chrome/views/client_view.h" +#include "grit/theme_resources.h" + +namespace views { + +// An enumeration of bitmap resources used by this window. +enum { + FRAME_PART_BITMAP_FIRST = 0, // Must be first. + + // Window Controls. + FRAME_CLOSE_BUTTON_ICON, + FRAME_CLOSE_BUTTON_ICON_H, + FRAME_CLOSE_BUTTON_ICON_P, + FRAME_CLOSE_BUTTON_ICON_SA, + FRAME_CLOSE_BUTTON_ICON_SA_H, + FRAME_CLOSE_BUTTON_ICON_SA_P, + FRAME_RESTORE_BUTTON_ICON, + FRAME_RESTORE_BUTTON_ICON_H, + FRAME_RESTORE_BUTTON_ICON_P, + FRAME_MAXIMIZE_BUTTON_ICON, + FRAME_MAXIMIZE_BUTTON_ICON_H, + FRAME_MAXIMIZE_BUTTON_ICON_P, + FRAME_MINIMIZE_BUTTON_ICON, + FRAME_MINIMIZE_BUTTON_ICON_H, + FRAME_MINIMIZE_BUTTON_ICON_P, + + // Window Frame Border. + FRAME_BOTTOM_EDGE, + FRAME_BOTTOM_LEFT_CORNER, + FRAME_BOTTOM_RIGHT_CORNER, + FRAME_LEFT_EDGE, + FRAME_RIGHT_EDGE, + FRAME_TOP_EDGE, + FRAME_TOP_LEFT_CORNER, + FRAME_TOP_RIGHT_CORNER, + + // Client Edge Border. + FRAME_CLIENT_EDGE_TOP_LEFT, + FRAME_CLIENT_EDGE_TOP, + FRAME_CLIENT_EDGE_TOP_RIGHT, + FRAME_CLIENT_EDGE_RIGHT, + FRAME_CLIENT_EDGE_BOTTOM_RIGHT, + FRAME_CLIENT_EDGE_BOTTOM, + FRAME_CLIENT_EDGE_BOTTOM_LEFT, + FRAME_CLIENT_EDGE_LEFT, + + FRAME_PART_BITMAP_COUNT // Must be last. +}; + +class ActiveWindowResources : public WindowResources { + public: + ActiveWindowResources() { + InitClass(); + } + virtual ~ActiveWindowResources() { + } + + // WindowResources implementation: + virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { + return standard_frame_bitmaps_[part]; + } + + private: + static void InitClass() { + static bool initialized = false; + if (!initialized) { + static const int kFramePartBitmapIds[] = { + 0, + IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, + IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, + IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, + IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, + IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, + IDR_WINDOW_BOTTOM_CENTER, IDR_WINDOW_BOTTOM_LEFT_CORNER, + IDR_WINDOW_BOTTOM_RIGHT_CORNER, IDR_WINDOW_LEFT_SIDE, + IDR_WINDOW_RIGHT_SIDE, IDR_WINDOW_TOP_CENTER, + IDR_WINDOW_TOP_LEFT_CORNER, IDR_WINDOW_TOP_RIGHT_CORNER, + IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, + IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, + IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, + IDR_CONTENT_LEFT_SIDE, + 0 + }; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { + int id = kFramePartBitmapIds[i]; + if (id != 0) + standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); + } + initialized = true; + } + } + + static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; + static ChromeFont title_font_; + + DISALLOW_EVIL_CONSTRUCTORS(ActiveWindowResources); +}; + +class InactiveWindowResources : public WindowResources { + public: + InactiveWindowResources() { + InitClass(); + } + virtual ~InactiveWindowResources() { + } + + // WindowResources implementation: + virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { + return standard_frame_bitmaps_[part]; + } + + private: + static void InitClass() { + static bool initialized = false; + if (!initialized) { + static const int kFramePartBitmapIds[] = { + 0, + IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, + IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, + IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, + IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, + IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, + IDR_DEWINDOW_BOTTOM_CENTER, IDR_DEWINDOW_BOTTOM_LEFT_CORNER, + IDR_DEWINDOW_BOTTOM_RIGHT_CORNER, IDR_DEWINDOW_LEFT_SIDE, + IDR_DEWINDOW_RIGHT_SIDE, IDR_DEWINDOW_TOP_CENTER, + IDR_DEWINDOW_TOP_LEFT_CORNER, IDR_DEWINDOW_TOP_RIGHT_CORNER, + IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, + IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, + IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, + IDR_CONTENT_LEFT_SIDE, + 0 + }; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { + int id = kFramePartBitmapIds[i]; + if (id != 0) + standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); + } + initialized = true; + } + } + + static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; + + DISALLOW_EVIL_CONSTRUCTORS(InactiveWindowResources); +}; + +// static +SkBitmap* ActiveWindowResources::standard_frame_bitmaps_[]; +SkBitmap* InactiveWindowResources::standard_frame_bitmaps_[]; + +// static +WindowResources* DefaultNonClientView::active_resources_ = NULL; +WindowResources* DefaultNonClientView::inactive_resources_ = NULL; +ChromeFont DefaultNonClientView::title_font_; + +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 to less than 18 px tall, plus the height of the +// frame border and any bottom edge. +const int kTitlebarMinimumHeight = 18; +// The icon is inset 2 px from the left frame border. +const int kIconLeftSpacing = 2; +// The icon takes up 16/25th of the available titlebar height. (This is +// expressed as two ints to avoid precision losses leading to off-by-one pixel +// errors.) +const int kIconHeightFractionNumerator = 16; +const int kIconHeightFractionDenominator = 25; +// The icon never shrinks below 16 px on a side. +const int kIconMinimumSize = 16; +// Because our frame border has a different "3D look" than Windows', with a less +// cluttered top edge, we need to shift the icon up by 1 px in restored mode so +// it looks more centered. +const int kIconRestoredAdjust = 1; +// There is a 4 px gap between the icon and the title text. +const int kIconTitleSpacing = 4; +// The title text starts 2 px below the bottom of the top frame border. +const int kTitleTopSpacing = 2; +// There is a 5 px gap between the title text and the caption buttons. +const int kTitleCaptionSpacing = 5; +// The caption buttons are always drawn 1 px down from the visible top of the +// window (the true top in restored mode, or the top of the screen in maximized +// mode). +const int kCaptionTopSpacing = 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, public: + +DefaultNonClientView::DefaultNonClientView( + CustomFrameWindow* container) + : NonClientView(), + client_view_(NULL), + close_button_(new Button), + restore_button_(new Button), + maximize_button_(new Button), + minimize_button_(new Button), + system_menu_button_(new Button), + should_show_minmax_buttons_(false), + container_(container) { + InitClass(); + WindowResources* resources = active_resources_; + + // Close button images will be set in LayoutWindowControls(). + close_button_->SetListener(this, -1); + AddChildView(close_button_); + + restore_button_->SetImage(Button::BS_NORMAL, + resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON)); + restore_button_->SetImage(Button::BS_HOT, + resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_H)); + restore_button_->SetImage(Button::BS_PUSHED, + resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_P)); + restore_button_->SetListener(this, -1); + AddChildView(restore_button_); + + maximize_button_->SetImage(Button::BS_NORMAL, + resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON)); + maximize_button_->SetImage(Button::BS_HOT, + resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_H)); + maximize_button_->SetImage(Button::BS_PUSHED, + resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_P)); + maximize_button_->SetListener(this, -1); + AddChildView(maximize_button_); + + minimize_button_->SetImage(Button::BS_NORMAL, + resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON)); + minimize_button_->SetImage(Button::BS_HOT, + resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_H)); + minimize_button_->SetImage(Button::BS_PUSHED, + resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_P)); + minimize_button_->SetListener(this, -1); + AddChildView(minimize_button_); + + should_show_minmax_buttons_ = container->window_delegate()->CanMaximize(); + + AddChildView(system_menu_button_); +} + +DefaultNonClientView::~DefaultNonClientView() { +} + +/////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, CustomFrameWindow::NonClientView implementation: + +gfx::Rect DefaultNonClientView::CalculateClientAreaBounds(int width, + int height) const { + int top_height = NonClientTopBorderHeight(); + int border_thickness = NonClientBorderThickness(); + return gfx::Rect(border_thickness, top_height, + std::max(0, width - (2 * border_thickness)), + std::max(0, height - top_height - border_thickness)); +} + +gfx::Size DefaultNonClientView::CalculateWindowSizeForClientSize( + int width, + int height) const { + int border_thickness = NonClientBorderThickness(); + return gfx::Size(width + (2 * border_thickness), + height + NonClientTopBorderHeight() + border_thickness); +} + +gfx::Point DefaultNonClientView::GetSystemMenuPoint() const { + gfx::Point system_menu_point(FrameBorderThickness(), + NonClientTopBorderHeight() - BottomEdgeThicknessWithinNonClientHeight()); + ConvertPointToScreen(this, &system_menu_point); + return system_menu_point; +} + +int DefaultNonClientView::NonClientHitTest(const gfx::Point& point) { + if (!bounds().Contains(point)) + return HTNOWHERE; + + int frame_component = container_->client_view()->NonClientHitTest(point); + if (frame_component != HTNOWHERE) + return frame_component; + + // Then see if the point is within any of the window controls. + if (close_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) + return HTCLOSE; + if (restore_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( + point)) + return HTMAXBUTTON; + if (maximize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( + point)) + return HTMAXBUTTON; + if (minimize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( + point)) + return HTMINBUTTON; + if (system_menu_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( + point)) + return HTSYSMENU; + + int window_component = GetHTComponentForFrame(point, FrameBorderThickness(), + NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, + container_->window_delegate()->CanResize()); + // Fall back to the caption if no other component matches. + return (window_component == HTNOWHERE) ? HTCAPTION : window_component; +} + +void DefaultNonClientView::GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) { + DCHECK(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())); + window_mask->lineTo(0, SkIntToScalar(size.height())); + window_mask->close(); +} + +void DefaultNonClientView::EnableClose(bool enable) { + close_button_->SetEnabled(enable); +} + +void DefaultNonClientView::ResetWindowControls() { + restore_button_->SetState(Button::BS_NORMAL); + minimize_button_->SetState(Button::BS_NORMAL); + maximize_button_->SetState(Button::BS_NORMAL); + // The close button isn't affected by this constraint. +} + +/////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, View overrides: + +void DefaultNonClientView::Paint(ChromeCanvas* canvas) { + if (container_->IsMaximized()) + PaintMaximizedFrameBorder(canvas); + else + PaintRestoredFrameBorder(canvas); + PaintTitleBar(canvas); + if (!container_->IsMaximized()) + PaintRestoredClientEdge(canvas); +} + +void DefaultNonClientView::Layout() { + LayoutWindowControls(); + LayoutTitleBar(); + LayoutClientView(); +} + +void DefaultNonClientView::ViewHierarchyChanged(bool is_add, + View* parent, + View* child) { + // Add our Client View 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) + AddChildView(container_->client_view()); +} + +/////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, BaseButton::ButtonListener implementation: + +void DefaultNonClientView::ButtonPressed(BaseButton* sender) { + if (sender == close_button_) + container_->ExecuteSystemMenuCommand(SC_CLOSE); + else if (sender == minimize_button_) + container_->ExecuteSystemMenuCommand(SC_MINIMIZE); + else if (sender == maximize_button_) + container_->ExecuteSystemMenuCommand(SC_MAXIMIZE); + else if (sender == restore_button_) + container_->ExecuteSystemMenuCommand(SC_RESTORE); +} + +/////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, private: + +int DefaultNonClientView::FrameBorderThickness() const { + return container_->IsMaximized() ? + GetSystemMetrics(SM_CXSIZEFRAME) : kFrameBorderThickness; +} + +int DefaultNonClientView::NonClientBorderThickness() const { + // In maximized mode, we don't show a client edge. + return FrameBorderThickness() + + (container_->IsMaximized() ? 0 : kClientEdgeThickness); +} + +int DefaultNonClientView::NonClientTopBorderHeight() const { + int title_top_spacing, title_thickness; + return TitleCoordinates(&title_top_spacing, &title_thickness); +} + +int DefaultNonClientView::BottomEdgeThicknessWithinNonClientHeight() const { + return kFrameShadowThickness + + (container_->IsMaximized() ? 0 : kClientEdgeThickness); +} + +int DefaultNonClientView::TitleCoordinates(int* title_top_spacing, + int* title_thickness) const { + int frame_thickness = FrameBorderThickness(); + int min_titlebar_height = kTitlebarMinimumHeight + frame_thickness; + *title_top_spacing = frame_thickness + kTitleTopSpacing; + // The bottom spacing should be the same apparent height as the top spacing. + // Because the actual top spacing height varies based on the system border + // thickness, we calculate this based on the restored top spacing and then + // adjust for maximized mode. We also don't include the frame shadow here, + // since while it's part of the bottom spacing it will be added in at the end. + int title_bottom_spacing = + kFrameBorderThickness + kTitleTopSpacing - kFrameShadowThickness; + if (container_->IsMaximized()) { + // When we maximize, the top border appears to be chopped off; shift the + // title down to stay centered within the remaining space. + int title_adjust = (kFrameBorderThickness / 2); + *title_top_spacing += title_adjust; + title_bottom_spacing -= title_adjust; + } + *title_thickness = std::max(title_font_.height(), + min_titlebar_height - *title_top_spacing - title_bottom_spacing); + return *title_top_spacing + *title_thickness + title_bottom_spacing + + BottomEdgeThicknessWithinNonClientHeight(); +} + +void DefaultNonClientView::PaintRestoredFrameBorder(ChromeCanvas* canvas) { + SkBitmap* top_left_corner = resources()->GetPartBitmap(FRAME_TOP_LEFT_CORNER); + SkBitmap* top_right_corner = + resources()->GetPartBitmap(FRAME_TOP_RIGHT_CORNER); + SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); + SkBitmap* right_edge = resources()->GetPartBitmap(FRAME_RIGHT_EDGE); + SkBitmap* left_edge = resources()->GetPartBitmap(FRAME_LEFT_EDGE); + SkBitmap* bottom_left_corner = + resources()->GetPartBitmap(FRAME_BOTTOM_LEFT_CORNER); + SkBitmap* bottom_right_corner = + resources()->GetPartBitmap(FRAME_BOTTOM_RIGHT_CORNER); + SkBitmap* bottom_edge = resources()->GetPartBitmap(FRAME_BOTTOM_EDGE); + + // 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 DefaultNonClientView::PaintMaximizedFrameBorder( + ChromeCanvas* canvas) { + SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); + canvas->TileImageInt(*top_edge, 0, FrameBorderThickness(), width(), + top_edge->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 = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); + int edge_height = titlebar_bottom->height() - kClientEdgeThickness; + canvas->TileImageInt(*titlebar_bottom, 0, + container_->client_view()->y() - edge_height, width(), edge_height); +} + +void DefaultNonClientView::PaintTitleBar(ChromeCanvas* canvas) { + WindowDelegate* d = container_->window_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, + MirroredLeftPointForRect(title_bounds_), title_bounds_.y(), + title_bounds_.width(), title_bounds_.height()); +} + +void DefaultNonClientView::PaintRestoredClientEdge(ChromeCanvas* canvas) { + gfx::Rect client_area_bounds = container_->client_view()->bounds(); + int client_area_top = client_area_bounds.y(); + + SkBitmap* top_left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT); + SkBitmap* top = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); + SkBitmap* top_right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_RIGHT); + SkBitmap* right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_RIGHT); + SkBitmap* bottom_right = + resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_RIGHT); + SkBitmap* bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM); + SkBitmap* bottom_left = + resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_LEFT); + SkBitmap* left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_LEFT); + + // Top. + // This next calculation is necessary because the top center bitmap is shorter + // than the top left and right bitmaps. We need their top edges to line up, + // and we need the left and right edges to start below the corners' bottoms. + int top_edge_y = client_area_top - top->height(); + client_area_top = top_edge_y + top_left->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); +} + +void DefaultNonClientView::LayoutWindowControls() { + close_button_->SetImageAlignment(Button::ALIGN_LEFT, Button::ALIGN_BOTTOM); + // 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. + bool is_maximized = container_->IsMaximized(); + int frame_thickness = FrameBorderThickness(); + int caption_y = is_maximized ? frame_thickness : kCaptionTopSpacing; + int top_extra_height = is_maximized ? kCaptionTopSpacing : 0; + // 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; + int right_spacing = is_maximized ? + (GetSystemMetrics(SM_CXSIZEFRAME) + right_extra_width) : frame_thickness; + gfx::Size close_button_size = close_button_->GetPreferredSize(); + close_button_->SetBounds(width() - close_button_size.width() - right_spacing, + caption_y, + close_button_size.width() + right_extra_width, + close_button_size.height() + top_extra_height); + + // When the window is restored, we show a maximized button; otherwise, we show + // a restore button. + bool is_restored = !is_maximized && !container_->IsMinimized(); + views::Button* invisible_button = is_restored ? + restore_button_ : maximize_button_; + invisible_button->SetVisible(false); + + views::Button* 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(Button::ALIGN_LEFT, Button::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() + top_extra_height); + + minimize_button_->SetVisible(true); + minimize_button_->SetImageAlignment(Button::ALIGN_LEFT, + Button::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() + top_extra_height); + + normal_part = FRAME_CLOSE_BUTTON_ICON; + hot_part = FRAME_CLOSE_BUTTON_ICON_H; + pushed_part = FRAME_CLOSE_BUTTON_ICON_P; + } else { + visible_button->SetVisible(false); + minimize_button_->SetVisible(false); + + normal_part = FRAME_CLOSE_BUTTON_ICON_SA; + hot_part = FRAME_CLOSE_BUTTON_ICON_SA_H; + pushed_part = FRAME_CLOSE_BUTTON_ICON_SA_P; + } + + close_button_->SetImage(Button::BS_NORMAL, + active_resources_->GetPartBitmap(normal_part)); + close_button_->SetImage(Button::BS_HOT, + active_resources_->GetPartBitmap(hot_part)); + close_button_->SetImage(Button::BS_PUSHED, + active_resources_->GetPartBitmap(pushed_part)); +} + +void DefaultNonClientView::LayoutTitleBar() { + // Always lay out the icon, even when it's not present, so we can lay out the + // window title based on its position. + int frame_thickness = FrameBorderThickness(); + int icon_x = frame_thickness + kIconLeftSpacing; + + // The usable height of the titlebar area is the total height minus the top + // resize border and any edge area we draw at its bottom. + int title_top_spacing, title_thickness; + int top_height = TitleCoordinates(&title_top_spacing, &title_thickness); + int available_height = top_height - frame_thickness - + BottomEdgeThicknessWithinNonClientHeight(); + + // The icon takes up a constant fraction of the available height, down to a + // minimum size, and is always an even number of pixels on a side (presumably + // to make scaled icons look better). It's centered within the usable height. + int icon_size = std::max((available_height * kIconHeightFractionNumerator / + kIconHeightFractionDenominator) / 2 * 2, kIconMinimumSize); + int icon_y = ((available_height - icon_size) / 2) + frame_thickness; + + // Hack: 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 we come up to compensate. + if (!container_->IsMaximized()) + icon_y -= kIconRestoredAdjust; + + views::WindowDelegate* d = container_->window_delegate(); + if (!d->ShouldShowWindowIcon()) + icon_size = 0; + system_menu_button_->SetBounds(icon_x, icon_y, icon_size, icon_size); + + // Size the title. + int icon_right = icon_x + icon_size; + int title_x = + icon_right + (d->ShouldShowWindowIcon() ? kIconTitleSpacing : 0); + int title_right = (should_show_minmax_buttons_ ? + minimize_button_->x() : close_button_->x()) - kTitleCaptionSpacing; + title_bounds_.SetRect(title_x, + title_top_spacing + ((title_thickness - title_font_.height()) / 2), + std::max(0, title_right - title_x), title_font_.height()); +} + +void DefaultNonClientView::LayoutClientView() { + container_->client_view()->SetBounds(CalculateClientAreaBounds(width(), + height())); +} + +// static +void DefaultNonClientView::InitClass() { + static bool initialized = false; + if (!initialized) { + active_resources_ = new ActiveWindowResources; + inactive_resources_ = new InactiveWindowResources; + + title_font_ = win_util::GetWindowTitleFont(); + + initialized = true; + } +} + +} // namespace views diff --git a/chrome/views/default_non_client_view.h b/chrome/views/default_non_client_view.h new file mode 100644 index 0000000..afc86c2 --- /dev/null +++ b/chrome/views/default_non_client_view.h @@ -0,0 +1,125 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_VIEWS_DEFAULT_NON_CLIENT_VIEW_H_ +#define CHROME_VIEWS_DEFAULT_NON_CLIENT_VIEW_H_ + +#include "chrome/views/button.h" +#include "chrome/views/custom_frame_window.h" +#include "chrome/views/non_client_view.h" +#include "chrome/views/window_resources.h" + +namespace gfx{ +class Size; +class Path; +class Point; +} +class ChromeCanvas; + +namespace views { + +class ClientView; + +/////////////////////////////////////////////////////////////////////////////// +// +// DefaultNonClientView +// +// A ChromeView that provides the "frame" for CustomFrameWindows. This means +// rendering the non-standard window caption, border, and controls. +// +//////////////////////////////////////////////////////////////////////////////// +class DefaultNonClientView : public NonClientView, + public BaseButton::ButtonListener { + public: + explicit DefaultNonClientView(CustomFrameWindow* container); + virtual ~DefaultNonClientView(); + + // Overridden from views::NonClientView: + virtual gfx::Rect CalculateClientAreaBounds(int width, int height) const; + virtual gfx::Size CalculateWindowSizeForClientSize(int width, + int height) const; + virtual gfx::Point GetSystemMenuPoint() const; + virtual int NonClientHitTest(const gfx::Point& point); + virtual void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask); + virtual void EnableClose(bool enable); + virtual void ResetWindowControls(); + + // View overrides: + virtual void Paint(ChromeCanvas* canvas); + virtual void Layout(); + virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child); + + // BaseButton::ButtonListener implementation: + virtual void ButtonPressed(BaseButton* sender); + + 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; + + // A bottom border, and, in restored mode, a client edge are drawn at the + // bottom of the titlebar. This returns the total height drawn. + int BottomEdgeThicknessWithinNonClientHeight() const; + + // Calculates multiple values related to title layout. Returns the height of + // the entire titlebar including any connected client edge. + int TitleCoordinates(int* title_top_spacing, + int* title_thickness) const; + + // Paint various sub-components of this view. + void PaintRestoredFrameBorder(ChromeCanvas* canvas); + void PaintMaximizedFrameBorder(ChromeCanvas* canvas); + void PaintTitleBar(ChromeCanvas* canvas); + void PaintRestoredClientEdge(ChromeCanvas* canvas); + + // Layout various sub-components of this view. + void LayoutWindowControls(); + void LayoutTitleBar(); + void LayoutClientView(); + + // Returns the resource collection to be used when rendering the window. + WindowResources* resources() const { + return container_->is_active() || paint_as_active() ? active_resources_ + : inactive_resources_; + } + + // The View that provides the background for the window, and optionally + // dialog buttons. Note: the non-client view does _not_ own this view, the + // container does. + ClientView* client_view_; + + // The layout rect of the title, if visible. + gfx::Rect title_bounds_; + + // Window controls. + Button* close_button_; + Button* restore_button_; + Button* maximize_button_; + Button* minimize_button_; + Button* system_menu_button_; // Uses the window icon if visible. + bool should_show_minmax_buttons_; + + // The window that owns this view. + CustomFrameWindow* container_; + + // Initialize various static resources. + static void InitClass(); + static WindowResources* active_resources_; + static WindowResources* inactive_resources_; + static ChromeFont title_font_; + + DISALLOW_EVIL_CONSTRUCTORS(DefaultNonClientView); +}; + +} // namespace views + +#endif // #ifndef CHROME_VIEWS_DEFAULT_NON_CLIENT_VIEW_H_ diff --git a/chrome/views/views.vcproj b/chrome/views/views.vcproj index 9656303..e937b47 100644 --- a/chrome/views/views.vcproj +++ b/chrome/views/views.vcproj @@ -270,6 +270,14 @@ > </File> <File + RelativePath=".\default_non_client_view.cc" + > + </File> + <File + RelativePath=".\default_non_client_view.h" + > + </File> + <File RelativePath=".\dialog_client_view.cc" > </File> |