summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-15 04:54:30 +0000
committerjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-15 04:54:30 +0000
commitf3d7d9b6dc04aa5adb57dd7699b9d395e1dbf640 (patch)
tree600633c97fd77b4b280e28491c99fd1a1697b64d /apps
parenta29040990b3d14ad32979a195e9f579c4ef89251 (diff)
downloadchromium_src-f3d7d9b6dc04aa5adb57dd7699b9d395e1dbf640.zip
chromium_src-f3d7d9b6dc04aa5adb57dd7699b9d395e1dbf640.tar.gz
chromium_src-f3d7d9b6dc04aa5adb57dd7699b9d395e1dbf640.tar.bz2
apps: Move chrome's ShellWindowFrameView into apps/
BUG=306535 TEST=compiles, can still resize a v2 app (like "Text") TBR=sky@chromium.org for moving shell_window_frame_view.h/cc out of its old location in Chrome Review URL: https://codereview.chromium.org/27029006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@228619 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'apps')
-rw-r--r--apps/apps.gypi12
-rw-r--r--apps/native_app_window.h7
-rw-r--r--apps/ui/views/DEPS6
-rw-r--r--apps/ui/views/shell_window_frame_view.cc361
-rw-r--r--apps/ui/views/shell_window_frame_view.h94
5 files changed, 480 insertions, 0 deletions
diff --git a/apps/apps.gypi b/apps/apps.gypi
index e44dc21..62f6d896 100644
--- a/apps/apps.gypi
+++ b/apps/apps.gypi
@@ -70,6 +70,8 @@
'shell_window_registry.h',
'switches.cc',
'switches.h',
+ 'ui/views/shell_window_frame_view.cc',
+ 'ui/views/shell_window_frame_view.h',
],
'conditions': [
['chromeos==1',
@@ -86,6 +88,16 @@
],
}
],
+ ['toolkit_views==1', {
+ 'dependencies': [
+ '../ui/base/strings/ui_strings.gyp:ui_strings',
+ '../ui/views/views.gyp:views',
+ ],
+ }, { # toolkit_views==0
+ 'sources/': [
+ ['exclude', 'ui/views/'],
+ ],
+ }],
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [ 4267, ],
diff --git a/apps/native_app_window.h b/apps/native_app_window.h
index e5403ed..8344914 100644
--- a/apps/native_app_window.h
+++ b/apps/native_app_window.h
@@ -22,6 +22,9 @@ class NativeAppWindow : public ui::BaseWindow,
virtual void UpdateDraggableRegions(
const std::vector<extensions::DraggableRegion>& regions) = 0;
+ // Returns the region used by frameless windows for dragging. May return NULL.
+ virtual SkRegion* GetDraggableRegion() = 0;
+
// Called when the region that accepts input events is changed.
// If |region| is NULL, then the entire window will accept input events.
virtual void UpdateInputRegion(scoped_ptr<SkRegion> region) = 0;
@@ -43,6 +46,10 @@ class NativeAppWindow : public ui::BaseWindow,
virtual void HandleKeyboardEvent(
const content::NativeWebKeyboardEvent& event) = 0;
+ // Returns true if the window has no frame, as for a window opened by
+ // chrome.app.window.create with the option 'frame' set to 'none'.
+ virtual bool IsFrameless() const = 0;
+
// Returns the difference between the window bounds (including titlebar and
// borders) and the content bounds, if any.
virtual gfx::Insets GetFrameInsets() const = 0;
diff --git a/apps/ui/views/DEPS b/apps/ui/views/DEPS
new file mode 100644
index 0000000..e0bd7db
--- /dev/null
+++ b/apps/ui/views/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ # TODO(jamescook): Eliminate this by introducing apps_resources.grd, see
+ # crbug.com/306688
+ "+grit/theme_resources.h",
+ "+grit/ui_strings.h",
+]
diff --git a/apps/ui/views/shell_window_frame_view.cc b/apps/ui/views/shell_window_frame_view.cc
new file mode 100644
index 0000000..971cb48
--- /dev/null
+++ b/apps/ui/views/shell_window_frame_view.cc
@@ -0,0 +1,361 @@
+// Copyright 2013 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 "apps/ui/views/shell_window_frame_view.h"
+
+#include "apps/native_app_window.h"
+#include "base/strings/utf_string_conversions.h"
+#include "extensions/common/draggable_region.h"
+#include "grit/theme_resources.h"
+#include "grit/ui_strings.h" // Accessibility names
+#include "third_party/skia/include/core/SkPaint.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/path.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/views_delegate.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#endif
+
+namespace {
+// Height of the chrome-style caption, in pixels.
+const int kCaptionHeight = 25;
+} // namespace
+
+namespace apps {
+
+const char ShellWindowFrameView::kViewClassName[] =
+ "browser/ui/views/extensions/ShellWindowFrameView";
+
+ShellWindowFrameView::ShellWindowFrameView(NativeAppWindow* window)
+ : window_(window),
+ frame_(NULL),
+ close_button_(NULL),
+ maximize_button_(NULL),
+ restore_button_(NULL),
+ minimize_button_(NULL),
+ resize_inside_bounds_size_(0),
+ resize_area_corner_size_(0) {
+}
+
+ShellWindowFrameView::~ShellWindowFrameView() {
+}
+
+void ShellWindowFrameView::Init(views::Widget* frame,
+ int resize_inside_bounds_size,
+ int resize_outside_bounds_size,
+ int resize_outside_scale_for_touch,
+ int resize_area_corner_size) {
+ frame_ = frame;
+ resize_inside_bounds_size_ = resize_inside_bounds_size;
+ resize_area_corner_size_ = resize_area_corner_size;
+
+ if (!window_->IsFrameless()) {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ close_button_ = new views::ImageButton(this);
+ close_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE).ToImageSkia());
+ close_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_H).ToImageSkia());
+ close_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_P).ToImageSkia());
+ close_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
+ AddChildView(close_button_);
+ maximize_button_ = new views::ImageButton(this);
+ maximize_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE).ToImageSkia());
+ maximize_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_H).ToImageSkia());
+ maximize_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_P).ToImageSkia());
+ maximize_button_->SetImage(views::CustomButton::STATE_DISABLED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MAXIMIZE_D).ToImageSkia());
+ maximize_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
+ AddChildView(maximize_button_);
+ restore_button_ = new views::ImageButton(this);
+ restore_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE).ToImageSkia());
+ restore_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_H).ToImageSkia());
+ restore_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_RESTORE_P).ToImageSkia());
+ restore_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_RESTORE));
+ AddChildView(restore_button_);
+ minimize_button_ = new views::ImageButton(this);
+ minimize_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE).ToImageSkia());
+ minimize_button_->SetImage(views::CustomButton::STATE_HOVERED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_H).ToImageSkia());
+ minimize_button_->SetImage(views::CustomButton::STATE_PRESSED,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_MINIMIZE_P).ToImageSkia());
+ minimize_button_->SetAccessibleName(
+ l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
+ AddChildView(minimize_button_);
+ }
+
+#if defined(USE_AURA)
+ aura::Window* window = frame->GetNativeWindow();
+ // Some Aura implementations (Ash) allow resize handles outside the window.
+ if (resize_outside_bounds_size > 0) {
+ gfx::Insets mouse_insets = gfx::Insets(-resize_outside_bounds_size,
+ -resize_outside_bounds_size,
+ -resize_outside_bounds_size,
+ -resize_outside_bounds_size);
+ gfx::Insets touch_insets =
+ mouse_insets.Scale(resize_outside_scale_for_touch);
+ // Ensure we get resize cursors for a few pixels outside our bounds.
+ window->SetHitTestBoundsOverrideOuter(mouse_insets, touch_insets);
+ }
+ // Ensure we get resize cursors just inside our bounds as well.
+ // TODO(jeremya): do we need to update these when in fullscreen/maximized?
+ window->set_hit_test_bounds_override_inner(
+ gfx::Insets(resize_inside_bounds_size_, resize_inside_bounds_size_,
+ resize_inside_bounds_size_, resize_inside_bounds_size_));
+#endif
+}
+
+// views::NonClientFrameView implementation.
+
+gfx::Rect ShellWindowFrameView::GetBoundsForClientView() const {
+ if (window_->IsFrameless() || frame_->IsFullscreen())
+ return bounds();
+ return gfx::Rect(0, kCaptionHeight, width(),
+ std::max(0, height() - kCaptionHeight));
+}
+
+gfx::Rect ShellWindowFrameView::GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const {
+ if (window_->IsFrameless()) {
+ gfx::Rect window_bounds = client_bounds;
+ // Enforce minimum size (1, 1) in case that client_bounds is passed with
+ // empty size. This could occur when the frameless window is being
+ // initialized.
+ if (window_bounds.IsEmpty()) {
+ window_bounds.set_width(1);
+ window_bounds.set_height(1);
+ }
+ return window_bounds;
+ }
+
+ int closeButtonOffsetX =
+ (kCaptionHeight - close_button_->height()) / 2;
+ int header_width = close_button_->width() + closeButtonOffsetX * 2;
+ return gfx::Rect(client_bounds.x(),
+ std::max(0, client_bounds.y() - kCaptionHeight),
+ std::max(header_width, client_bounds.width()),
+ client_bounds.height() + kCaptionHeight);
+}
+
+int ShellWindowFrameView::NonClientHitTest(const gfx::Point& point) {
+ if (frame_->IsFullscreen())
+ return HTCLIENT;
+
+ gfx::Rect expanded_bounds = bounds();
+#if defined(USE_AURA)
+ // Some Aura implementations (Ash) optionally allow resize handles just
+ // outside the window bounds.
+ aura::Window* window = frame_->GetNativeWindow();
+ if (aura::Env::GetInstance()->is_touch_down())
+ expanded_bounds.Inset(window->hit_test_bounds_override_outer_touch());
+ else
+ expanded_bounds.Inset(window->hit_test_bounds_override_outer_mouse());
+#endif
+ // Points outside the (possibly expanded) bounds can be discarded.
+ if (!expanded_bounds.Contains(point))
+ return HTNOWHERE;
+
+ // Check the frame first, as we allow a small area overlapping the contents
+ // to be used for resize handles.
+ bool can_ever_resize = frame_->widget_delegate() ?
+ frame_->widget_delegate()->CanResize() :
+ false;
+ // Don't allow overlapping resize handles when the window is maximized or
+ // fullscreen, as it can't be resized in those states.
+ int resize_border =
+ (frame_->IsMaximized() || frame_->IsFullscreen()) ? 0 :
+ resize_inside_bounds_size_;
+ int frame_component = GetHTComponentForFrame(point,
+ resize_border,
+ resize_border,
+ resize_area_corner_size_,
+ resize_area_corner_size_,
+ can_ever_resize);
+ if (frame_component != HTNOWHERE)
+ return frame_component;
+
+ // Check for possible draggable region in the client area for the frameless
+ // window.
+ if (window_->IsFrameless()) {
+ SkRegion* draggable_region = window_->GetDraggableRegion();
+ if (draggable_region && draggable_region->contains(point.x(), point.y()))
+ return HTCAPTION;
+ }
+
+ int client_component = frame_->client_view()->NonClientHitTest(point);
+ if (client_component != HTNOWHERE)
+ return client_component;
+
+ // Then see if the point is within any of the window controls.
+ if (close_button_ && close_button_->visible() &&
+ close_button_->GetMirroredBounds().Contains(point))
+ return HTCLOSE;
+
+ // Caption is a safe default.
+ return HTCAPTION;
+}
+
+void ShellWindowFrameView::GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) {
+ // We got nothing to say about no window mask.
+}
+
+// views::View implementation.
+
+gfx::Size ShellWindowFrameView::GetPreferredSize() {
+ gfx::Size pref = frame_->client_view()->GetPreferredSize();
+ gfx::Rect bounds(0, 0, pref.width(), pref.height());
+ return frame_->non_client_view()->GetWindowBoundsForClientBounds(
+ bounds).size();
+}
+
+void ShellWindowFrameView::Layout() {
+ if (window_->IsFrameless())
+ return;
+ gfx::Size close_size = close_button_->GetPreferredSize();
+ const int kButtonOffsetY = 0;
+ const int kButtonSpacing = 1;
+ const int kRightMargin = 3;
+
+ close_button_->SetBounds(
+ width() - kRightMargin - close_size.width(),
+ kButtonOffsetY,
+ close_size.width(),
+ close_size.height());
+
+ bool can_ever_resize = frame_->widget_delegate() ?
+ frame_->widget_delegate()->CanResize() :
+ false;
+ maximize_button_->SetEnabled(can_ever_resize);
+ gfx::Size maximize_size = maximize_button_->GetPreferredSize();
+ maximize_button_->SetBounds(
+ close_button_->x() - kButtonSpacing - maximize_size.width(),
+ kButtonOffsetY,
+ maximize_size.width(),
+ maximize_size.height());
+ gfx::Size restore_size = restore_button_->GetPreferredSize();
+ restore_button_->SetBounds(
+ close_button_->x() - kButtonSpacing - restore_size.width(),
+ kButtonOffsetY,
+ restore_size.width(),
+ restore_size.height());
+
+ bool maximized = frame_->IsMaximized();
+ maximize_button_->SetVisible(!maximized);
+ restore_button_->SetVisible(maximized);
+ if (maximized)
+ maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
+ else
+ restore_button_->SetState(views::CustomButton::STATE_NORMAL);
+
+ gfx::Size minimize_size = minimize_button_->GetPreferredSize();
+ minimize_button_->SetBounds(
+ maximize_button_->x() - kButtonSpacing - minimize_size.width(),
+ kButtonOffsetY,
+ minimize_size.width(),
+ minimize_size.height());
+}
+
+void ShellWindowFrameView::OnPaint(gfx::Canvas* canvas) {
+ if (window_->IsFrameless())
+ return;
+
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ if (ShouldPaintAsActive()) {
+ close_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE).ToImageSkia());
+ } else {
+ close_button_->SetImage(views::CustomButton::STATE_NORMAL,
+ rb.GetNativeImageNamed(IDR_APP_WINDOW_CLOSE_U).ToImageSkia());
+ }
+
+ // TODO(jeremya): different look for inactive?
+ SkPaint paint;
+ paint.setAntiAlias(false);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorWHITE);
+ gfx::Path path;
+ const int radius = frame_->IsMaximized() ? 0 : 1;
+ path.moveTo(0, radius);
+ path.lineTo(radius, 0);
+ path.lineTo(width() - radius - 1, 0);
+ path.lineTo(width(), radius + 1);
+ path.lineTo(width(), kCaptionHeight);
+ path.lineTo(0, kCaptionHeight);
+ path.close();
+ canvas->DrawPath(path, paint);
+}
+
+const char* ShellWindowFrameView::GetClassName() const {
+ return kViewClassName;
+}
+
+gfx::Size ShellWindowFrameView::GetMinimumSize() {
+ gfx::Size min_size = frame_->client_view()->GetMinimumSize();
+ if (window_->IsFrameless())
+ return min_size;
+
+ // Ensure we can display the top of the caption area.
+ gfx::Rect client_bounds = GetBoundsForClientView();
+ min_size.Enlarge(0, client_bounds.y());
+ // Ensure we have enough space for the window icon and buttons. We allow
+ // the title string to collapse to zero width.
+ int closeButtonOffsetX =
+ (kCaptionHeight - close_button_->height()) / 2;
+ int header_width = close_button_->width() + closeButtonOffsetX * 2;
+ if (header_width > min_size.width())
+ min_size.set_width(header_width);
+ return min_size;
+}
+
+gfx::Size ShellWindowFrameView::GetMaximumSize() {
+ gfx::Size max_size = frame_->client_view()->GetMaximumSize();
+
+ // Add to the client maximum size the height of any title bar and borders.
+ gfx::Size client_size = GetBoundsForClientView().size();
+ if (max_size.width())
+ max_size.Enlarge(width() - client_size.width(), 0);
+ if (max_size.height())
+ max_size.Enlarge(0, height() - client_size.height());
+
+ return max_size;
+}
+
+// views::ButtonListener implementation.
+
+void ShellWindowFrameView::ButtonPressed(views::Button* sender,
+ const ui::Event& event) {
+ DCHECK(!window_->IsFrameless());
+ if (sender == close_button_)
+ frame_->Close();
+ else if (sender == maximize_button_)
+ frame_->Maximize();
+ else if (sender == restore_button_)
+ frame_->Restore();
+ else if (sender == minimize_button_)
+ frame_->Minimize();
+}
+
+} // namespace apps
diff --git a/apps/ui/views/shell_window_frame_view.h b/apps/ui/views/shell_window_frame_view.h
new file mode 100644
index 0000000..7fa10f5
--- /dev/null
+++ b/apps/ui/views/shell_window_frame_view.h
@@ -0,0 +1,94 @@
+// Copyright 2013 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 APPS_UI_VIEWS_SHELL_WINDOW_FRAME_VIEW_H_
+#define APPS_UI_VIEWS_SHELL_WINDOW_FRAME_VIEW_H_
+
+#include <string>
+
+#include "ui/gfx/path.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/window/non_client_view.h"
+
+namespace gfx {
+class Canvas;
+class Point;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace views {
+class ImageButton;
+class Widget;
+}
+
+namespace apps {
+
+class NativeAppWindow;
+
+// A frameless or non-Ash, non-panel NonClientFrameView for app windows.
+class ShellWindowFrameView : public views::NonClientFrameView,
+ public views::ButtonListener {
+ public:
+ static const char kViewClassName[];
+
+ explicit ShellWindowFrameView(NativeAppWindow* window);
+ virtual ~ShellWindowFrameView();
+
+ // Initializes this for the window |frame|. Sets the number of pixels for
+ // which a click is interpreted as a resize for the inner and outer border of
+ // the window and the lower-right corner resize handle.
+ void Init(views::Widget* frame,
+ int resize_inside_bounds_size,
+ int resize_outside_bounds_size,
+ int resize_outside_scale_for_touch,
+ int resize_area_corner_size);
+
+ private:
+ // views::NonClientFrameView implementation.
+ virtual gfx::Rect GetBoundsForClientView() const OVERRIDE;
+ virtual gfx::Rect GetWindowBoundsForClientBounds(
+ const gfx::Rect& client_bounds) const OVERRIDE;
+ virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE;
+ virtual void GetWindowMask(const gfx::Size& size,
+ gfx::Path* window_mask) OVERRIDE;
+ virtual void ResetWindowControls() OVERRIDE {}
+ virtual void UpdateWindowIcon() OVERRIDE {}
+ virtual void UpdateWindowTitle() OVERRIDE {}
+
+ // views::View implementation.
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void Layout() OVERRIDE;
+ virtual const char* GetClassName() const OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual gfx::Size GetMinimumSize() OVERRIDE;
+ virtual gfx::Size GetMaximumSize() OVERRIDE;
+
+ // views::ButtonListener implementation.
+ virtual void ButtonPressed(views::Button* sender, const ui::Event& event)
+ OVERRIDE;
+
+ NativeAppWindow* window_;
+ views::Widget* frame_;
+ views::ImageButton* close_button_;
+ views::ImageButton* maximize_button_;
+ views::ImageButton* restore_button_;
+ views::ImageButton* minimize_button_;
+
+ // Allow resize for clicks this many pixels inside the bounds.
+ int resize_inside_bounds_size_;
+
+ // Size in pixels of the lower-right corner resize handle.
+ int resize_area_corner_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellWindowFrameView);
+};
+
+} // namespace apps
+
+#endif // APPS_UI_VIEWS_SHELL_WINDOW_FRAME_VIEW_H_