summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ui/app_list/app_list.gyp3
-rw-r--r--ui/app_list/app_list_constants.cc3
-rw-r--r--ui/app_list/app_list_constants.h3
-rw-r--r--ui/app_list/cocoa/app_list_view_controller.h21
-rw-r--r--ui/app_list/cocoa/app_list_view_controller.mm101
-rw-r--r--ui/app_list/cocoa/app_list_view_controller_unittest.mm9
-rw-r--r--ui/app_list/cocoa/apps_grid_controller.h4
-rw-r--r--ui/app_list/cocoa/apps_grid_controller.mm7
-rw-r--r--ui/app_list/cocoa/apps_grid_controller_unittest.mm8
-rw-r--r--ui/app_list/cocoa/apps_search_box_controller.h52
-rw-r--r--ui/app_list/cocoa/apps_search_box_controller.mm293
-rw-r--r--ui/app_list/cocoa/apps_search_box_controller_unittest.mm123
-rw-r--r--ui/app_list/cocoa/test/apps_grid_controller_test_helper.h7
-rw-r--r--ui/app_list/cocoa/test/apps_grid_controller_test_helper.mm15
-rw-r--r--ui/app_list/views/app_list_background.cc5
15 files changed, 599 insertions, 55 deletions
diff --git a/ui/app_list/app_list.gyp b/ui/app_list/app_list.gyp
index b1128c1..60f21cf 100644
--- a/ui/app_list/app_list.gyp
+++ b/ui/app_list/app_list.gyp
@@ -50,6 +50,8 @@
'cocoa/apps_grid_view_item.h',
'cocoa/apps_grid_view_item.mm',
'cocoa/apps_pagination_model_observer.h',
+ 'cocoa/apps_search_box_controller.h',
+ 'cocoa/apps_search_box_controller.mm',
'cocoa/item_drag_controller.h',
'cocoa/item_drag_controller.mm',
'cocoa/scroll_view_with_no_scrollbars.h',
@@ -155,6 +157,7 @@
'cocoa/app_list_view_controller_unittest.mm',
'cocoa/app_list_window_controller_unittest.mm',
'cocoa/apps_grid_controller_unittest.mm',
+ 'cocoa/apps_search_box_controller_unittest.mm',
'cocoa/test/apps_grid_controller_test_helper.h',
'cocoa/test/apps_grid_controller_test_helper.mm',
'views/apps_grid_view_unittest.cc',
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index 116dab6..a83c7dd 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -7,6 +7,9 @@
namespace app_list {
const SkColor kContentsBackgroundColor = SkColorSetRGB(0xF5, 0xF5, 0xF5);
+const SkColor kSearchBoxBackground = SK_ColorWHITE;
+const SkColor kTopSeparatorColor = SkColorSetRGB(0xE5, 0xE5, 0xE5);
+
// 6% black over kContentsBackgroundColor
const SkColor kHighlightedColor = SkColorSetRGB(0xE6, 0xE6, 0xE6);
// 10% black over kContentsBackgroundColor
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index ac85f28..13d206e 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -12,6 +12,9 @@
namespace app_list {
APP_LIST_EXPORT extern const SkColor kContentsBackgroundColor;
+APP_LIST_EXPORT extern const SkColor kSearchBoxBackground;
+APP_LIST_EXPORT extern const SkColor kTopSeparatorColor;
+
APP_LIST_EXPORT extern const SkColor kHighlightedColor;
APP_LIST_EXPORT extern const SkColor kSelectedColor;
diff --git a/ui/app_list/cocoa/app_list_view_controller.h b/ui/app_list/cocoa/app_list_view_controller.h
index 80095423..40d68e3 100644
--- a/ui/app_list/cocoa/app_list_view_controller.h
+++ b/ui/app_list/cocoa/app_list_view_controller.h
@@ -11,23 +11,27 @@
#include "base/memory/scoped_ptr.h"
#include "ui/app_list/app_list_export.h"
#import "ui/app_list/cocoa/apps_pagination_model_observer.h"
+#import "ui/app_list/cocoa/apps_search_box_controller.h"
namespace app_list {
class AppListViewDelegate;
+class AppListModel;
}
-@class AppsGridController;
@class AppListPagerView;
+@class AppsGridController;
// Controller for the top-level view of the app list UI. It creates and hosts an
-// AppsGridController (displaying an AppListModel), and pager control for
-// navigating between pages in the grid.
+// AppsGridController (displaying an AppListModel), pager control to navigate
+// between pages in the grid, and search entry box.
APP_LIST_EXPORT
-@interface AppListViewController :
- NSViewController<AppsPaginationModelObserver, NSTextFieldDelegate> {
+@interface AppListViewController : NSViewController<AppsPaginationModelObserver,
+ AppsSearchBoxDelegate> {
@private
scoped_nsobject<AppsGridController> appsGridController_;
scoped_nsobject<AppListPagerView> pagerControl_;
+ scoped_nsobject<AppsSearchBoxController> appsSearchBoxController_;
+ scoped_nsobject<NSView> contentsView_;
scoped_ptr<app_list::AppListViewDelegate> delegate_;
}
@@ -41,4 +45,11 @@ APP_LIST_EXPORT
@end
+@interface AppListViewController (TestingAPI)
+
+- (void)setDelegate:(scoped_ptr<app_list::AppListViewDelegate>)newDelegate
+ withTestModel:(scoped_ptr<app_list::AppListModel>)newModel;
+
+@end
+
#endif // UI_APP_LIST_COCOA_APP_LIST_VIEW_CONTROLLER_H_
diff --git a/ui/app_list/cocoa/app_list_view_controller.mm b/ui/app_list/cocoa/app_list_view_controller.mm
index 7bfd2d5..91d82da 100644
--- a/ui/app_list/cocoa/app_list_view_controller.mm
+++ b/ui/app_list/cocoa/app_list_view_controller.mm
@@ -7,9 +7,12 @@
#include "base/mac/foundation_util.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/app_list_model.h"
#include "ui/app_list/app_list_view_delegate.h"
#import "ui/app_list/cocoa/app_list_pager_view.h"
#import "ui/app_list/cocoa/apps_grid_controller.h"
+#import "ui/base/cocoa/flipped_view.h"
+#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
namespace {
@@ -19,13 +22,11 @@ const CGFloat kBubbleCornerRadius = 3;
// Height of the pager.
const CGFloat kPagerPreferredHeight = 57;
-// Padding between the top of the grid and the top of the view.
-// TODO(tapted): Update padding when the search entry control is added.
-const CGFloat kTopPadding = 16;
+// Height of separator line drawn between the searchbox and grid view.
+const CGFloat kTopSeparatorSize = 1;
-// Height of the search input. TODO(tapted): Make this visible when the search
-// input UI is written.
-const CGFloat kSearchInputHeight = 0;
+// Height of the search input.
+const CGFloat kSearchInputHeight = 48;
// Minimum margin on either side of the pager. If the pager grows beyond this,
// the segment size is reduced.
@@ -35,23 +36,29 @@ const CGFloat kMaxSegmentWidth = 80;
} // namespace
-@interface BackgroundView : NSView;
+@interface BackgroundView : FlippedView;
@end
@implementation BackgroundView
- (void)drawRect:(NSRect)dirtyRect {
- [NSGraphicsContext saveGraphicsState];
- [gfx::SkColorToCalibratedNSColor(app_list::kContentsBackgroundColor) set];
- [[NSBezierPath bezierPathWithRoundedRect:[self bounds]
+ gfx::ScopedNSGraphicsContextSaveGState context;
+ NSRect boundsRect = [self bounds];
+ NSRect searchAreaRect = NSMakeRect(0, 0,
+ NSWidth(boundsRect), kSearchInputHeight);
+ NSRect separatorRect = NSMakeRect(0, NSMaxY(searchAreaRect),
+ NSWidth(boundsRect), kTopSeparatorSize);
+
+ [[NSBezierPath bezierPathWithRoundedRect:boundsRect
xRadius:kBubbleCornerRadius
yRadius:kBubbleCornerRadius] addClip];
- NSRectFill([self bounds]);
- [NSGraphicsContext restoreGraphicsState];
-}
-- (BOOL)isFlipped {
- return YES;
+ [gfx::SkColorToCalibratedNSColor(app_list::kContentsBackgroundColor) set];
+ NSRectFill(boundsRect);
+ [gfx::SkColorToCalibratedNSColor(app_list::kSearchBoxBackground) set];
+ NSRectFill(searchAreaRect);
+ [gfx::SkColorToCalibratedNSColor(app_list::kTopSeparatorColor) set];
+ NSRectFill(separatorRect);
}
@end
@@ -97,9 +104,22 @@ const CGFloat kMaxSegmentWidth = 80;
return delegate_.get();
}
-- (void)setDelegate:(scoped_ptr<app_list::AppListViewDelegate>)newDelegate {
+- (void)setDelegate:(scoped_ptr<app_list::AppListViewDelegate>)newDelegate
+ withTestModel:(scoped_ptr<app_list::AppListModel>)newModel {
+ if (delegate_) {
+ // First clean up, in reverse order.
+ [appsSearchBoxController_ setDelegate:nil];
+ }
delegate_.reset(newDelegate.release());
[appsGridController_ setDelegate:delegate_.get()];
+ if (newModel.get())
+ [appsGridController_ setModel:newModel.Pass()];
+ [appsSearchBoxController_ setDelegate:self];
+}
+
+- (void)setDelegate:(scoped_ptr<app_list::AppListViewDelegate>)newDelegate {
+ [self setDelegate:newDelegate.Pass()
+ withTestModel:scoped_ptr<app_list::AppListModel>()];
}
-(void)loadAndSetView {
@@ -107,22 +127,23 @@ const CGFloat kMaxSegmentWidth = 80;
[pagerControl_ setTarget:appsGridController_];
[pagerControl_ setAction:@selector(onPagerClicked:)];
- [[appsGridController_ view] setFrameOrigin:NSMakePoint(0, kTopPadding)];
+ NSRect gridFrame = [[appsGridController_ view] frame];
+ NSRect contentsRect = NSMakeRect(0, kSearchInputHeight + kTopSeparatorSize,
+ NSWidth(gridFrame), NSHeight(gridFrame) + kPagerPreferredHeight -
+ [AppsGridController scrollerPadding]);
- NSRect backgroundRect = [[appsGridController_ view] bounds];
- backgroundRect.size.height += kPagerPreferredHeight;
+ contentsView_.reset([[FlippedView alloc] initWithFrame:contentsRect]);
scoped_nsobject<BackgroundView> backgroundView(
- [[BackgroundView alloc] initWithFrame:backgroundRect]);
-
- NSRect searchInputRect =
- NSMakeRect(0, 0, backgroundRect.size.width, kSearchInputHeight);
- scoped_nsobject<NSTextField> searchInput(
- [[NSTextField alloc] initWithFrame:searchInputRect]);
- [searchInput setDelegate:self];
-
- [backgroundView addSubview:[appsGridController_ view]];
- [backgroundView addSubview:pagerControl_];
- [backgroundView addSubview:searchInput];
+ [[BackgroundView alloc] initWithFrame:
+ NSMakeRect(0, 0, NSMaxX(contentsRect), NSMaxY(contentsRect))]);
+ appsSearchBoxController_.reset(
+ [[AppsSearchBoxController alloc] initWithFrame:
+ NSMakeRect(0, 0, NSWidth(contentsRect), kSearchInputHeight)]);
+
+ [contentsView_ addSubview:[appsGridController_ view]];
+ [contentsView_ addSubview:pagerControl_];
+ [backgroundView addSubview:contentsView_];
+ [backgroundView addSubview:[appsSearchBoxController_ view]];
[self setView:backgroundView];
}
@@ -130,7 +151,7 @@ const CGFloat kMaxSegmentWidth = 80;
size_t pageCount = [appsGridController_ pageCount];
[pagerControl_ setSegmentCount:pageCount];
- NSRect viewFrame = [[self view] bounds];
+ NSRect viewFrame = [[pagerControl_ superview] bounds];
CGFloat segmentWidth = std::min(
kMaxSegmentWidth,
(viewFrame.size.width - 2 * kMinPagerMargin) / pageCount);
@@ -163,9 +184,17 @@ const CGFloat kMaxSegmentWidth = 80;
return [pagerControl_ findAndHighlightSegmentAtLocation:locationInWindow];
}
+- (app_list::SearchBoxModel*)searchBoxModel {
+ app_list::AppListModel* appListModel = [appsGridController_ model];
+ return appListModel ? appListModel->search_box() : NULL;
+}
+
- (BOOL)control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)command {
+ // TODO(tapted): If showing search results, first pass up/down navigation to
+ // the search results controller.
+
// If anything has been written, let the search view handle it.
if ([[control stringValue] length] > 0)
return NO;
@@ -183,4 +212,14 @@ const CGFloat kMaxSegmentWidth = 80;
return [appsGridController_ handleCommandBySelector:command];
}
+- (void)modelTextDidChange {
+ app_list::SearchBoxModel* searchBoxModel = [self searchBoxModel];
+ if (!searchBoxModel || !delegate_)
+ return;
+
+ // TODO(tapted): If there is a non-empty query in |searchBoxModel| reveal the
+ // search results, and run delegate_->StartSearch(). Or, if the query is now
+ // empty, hide results and run delegate_->StopSearch().
+}
+
@end
diff --git a/ui/app_list/cocoa/app_list_view_controller_unittest.mm b/ui/app_list/cocoa/app_list_view_controller_unittest.mm
index eb3a807..566819e 100644
--- a/ui/app_list/cocoa/app_list_view_controller_unittest.mm
+++ b/ui/app_list/cocoa/app_list_view_controller_unittest.mm
@@ -19,10 +19,7 @@ class AppListViewControllerTest : public AppsGridControllerTestHelper {
virtual void SetUp() OVERRIDE {
app_list_view_controller_.reset([[AppListViewController alloc] init]);
- [app_list_view_controller_ setDelegate:
- delegate_.PassAs<AppListViewDelegate>()];
SetUpWithGridController([app_list_view_controller_ appsGridController]);
-
[[test_window() contentView] addSubview:[app_list_view_controller_ view]];
}
@@ -33,6 +30,12 @@ class AppListViewControllerTest : public AppsGridControllerTestHelper {
AppsGridControllerTestHelper::TearDown();
}
+ virtual void ResetModel(scoped_ptr<AppListModel> new_model) OVERRIDE {
+ scoped_ptr<AppListViewDelegate> delegate_(new AppListTestViewDelegate);
+ [app_list_view_controller_ setDelegate:delegate_.Pass()
+ withTestModel:new_model.Pass()];
+ }
+
protected:
scoped_nsobject<AppListViewController> app_list_view_controller_;
diff --git a/ui/app_list/cocoa/apps_grid_controller.h b/ui/app_list/cocoa/apps_grid_controller.h
index 4fb2d6e..7db9fbf 100644
--- a/ui/app_list/cocoa/apps_grid_controller.h
+++ b/ui/app_list/cocoa/apps_grid_controller.h
@@ -54,6 +54,10 @@ APP_LIST_EXPORT
+ (void)setScrollAnimationDuration:(NSTimeInterval)duration;
+// The amount the grid view has been extended to hold the sometimes present
+// invisible scroller that allows for gesture scrolling.
++ (CGFloat)scrollerPadding;
+
- (NSCollectionView*)collectionViewAtPageIndex:(size_t)pageIndex;
- (size_t)pageIndexForCollectionView:(NSCollectionView*)page;
diff --git a/ui/app_list/cocoa/apps_grid_controller.mm b/ui/app_list/cocoa/apps_grid_controller.mm
index 6a47ed4..6d556426 100644
--- a/ui/app_list/cocoa/apps_grid_controller.mm
+++ b/ui/app_list/cocoa/apps_grid_controller.mm
@@ -21,6 +21,7 @@ const int kFixedColumns = 4;
const int kItemsPerPage = kFixedRows * kFixedColumns;
// Padding space in pixels for fixed layout.
+const CGFloat kGridTopPadding = 1;
const CGFloat kLeftRightPadding = 16;
const CGFloat kScrollerPadding = 16;
@@ -142,6 +143,10 @@ class AppsGridDelegateBridge : public ui::ListModelObserver {
g_scroll_duration = duration;
}
++ (CGFloat)scrollerPadding {
+ return kScrollerPadding;
+}
+
@synthesize paginationObserver = paginationObserver_;
- (id)init {
@@ -338,7 +343,7 @@ class AppsGridDelegateBridge : public ui::ListModelObserver {
scoped_nsobject<PageContainerView> pagesContainer(
[[PageContainerView alloc] initWithFrame:NSZeroRect]);
- NSRect scrollFrame = NSMakeRect(0, 0, kViewWidth,
+ NSRect scrollFrame = NSMakeRect(0, kGridTopPadding, kViewWidth,
kViewHeight + kScrollerPadding);
scoped_nsobject<ScrollViewWithNoScrollbars> scrollView(
[[ScrollViewWithNoScrollbars alloc] initWithFrame:scrollFrame]);
diff --git a/ui/app_list/cocoa/apps_grid_controller_unittest.mm b/ui/app_list/cocoa/apps_grid_controller_unittest.mm
index fe52e8c..b5876bc 100644
--- a/ui/app_list/cocoa/apps_grid_controller_unittest.mm
+++ b/ui/app_list/cocoa/apps_grid_controller_unittest.mm
@@ -80,9 +80,14 @@ class AppsGridControllerTest : public AppsGridControllerTestHelper {
public:
AppsGridControllerTest() {}
+ AppListTestViewDelegate* delegate() {
+ return owned_delegate_.get();
+ }
+
virtual void SetUp() OVERRIDE {
owned_apps_grid_controller_.reset([[AppsGridController alloc] init]);
- [owned_apps_grid_controller_ setDelegate:delegate_.get()];
+ owned_delegate_.reset(new AppListTestViewDelegate);
+ [owned_apps_grid_controller_ setDelegate:owned_delegate_.get()];
AppsGridControllerTestHelper::SetUpWithGridController(
owned_apps_grid_controller_.get());
@@ -98,6 +103,7 @@ class AppsGridControllerTest : public AppsGridControllerTestHelper {
private:
scoped_nsobject<AppsGridController> owned_apps_grid_controller_;
+ scoped_ptr<AppListTestViewDelegate> owned_delegate_;
DISALLOW_COPY_AND_ASSIGN(AppsGridControllerTest);
};
diff --git a/ui/app_list/cocoa/apps_search_box_controller.h b/ui/app_list/cocoa/apps_search_box_controller.h
new file mode 100644
index 0000000..8f048e8
--- /dev/null
+++ b/ui/app_list/cocoa/apps_search_box_controller.h
@@ -0,0 +1,52 @@
+// 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 UI_APP_LIST_COCOA_APPS_SEARCH_BOX_CONTROLLER_H_
+#define UI_APP_LIST_COCOA_APPS_SEARCH_BOX_CONTROLLER_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/memory/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/app_list/app_list_export.h"
+
+namespace app_list {
+class SearchBoxModel;
+class SearchBoxModelObserverBridge;
+}
+
+@class SearchTextField;
+
+@protocol AppsSearchBoxDelegate<NSTextFieldDelegate>
+
+- (app_list::SearchBoxModel*)searchBoxModel;
+- (void)modelTextDidChange;
+
+@end
+
+// Controller for the search box in the topmost portion of the app list.
+APP_LIST_EXPORT
+@interface AppsSearchBoxController : NSViewController<NSTextFieldDelegate> {
+ @private
+ scoped_nsobject<SearchTextField> searchTextField_;
+ scoped_nsobject<NSImageView> searchImageView_;
+ scoped_ptr<app_list::SearchBoxModelObserverBridge> bridge_;
+
+ id<AppsSearchBoxDelegate> delegate_; // Weak. Owns us.
+}
+
+@property(assign, nonatomic) id<AppsSearchBoxDelegate> delegate;
+
+- (id)initWithFrame:(NSRect)frame;
+- (void)clearSearch;
+
+@end
+
+@interface AppsSearchBoxController (TestingAPI)
+
+- (NSTextField*)searchTextField;
+
+@end
+
+#endif // UI_APP_LIST_COCOA_APPS_SEARCH_BOX_CONTROLLER_H_
diff --git a/ui/app_list/cocoa/apps_search_box_controller.mm b/ui/app_list/cocoa/apps_search_box_controller.mm
new file mode 100644
index 0000000..ad35dba
--- /dev/null
+++ b/ui/app_list/cocoa/apps_search_box_controller.mm
@@ -0,0 +1,293 @@
+// 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.
+
+#import "ui/app_list/cocoa/apps_search_box_controller.h"
+
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h"
+#include "ui/app_list/search_box_model.h"
+#include "ui/app_list/search_box_model_observer.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia_util_mac.h"
+
+namespace {
+
+// Padding either side of the search icon.
+const CGFloat kPadding = 14;
+
+// Size of the search icon.
+const CGFloat kSearchIconDimension = 32;
+
+}
+
+@interface AppsSearchBoxController ()
+
+- (NSImageView*)searchImageView;
+- (void)addSubviews;
+
+@end
+
+namespace app_list {
+
+class SearchBoxModelObserverBridge : public SearchBoxModelObserver {
+ public:
+ SearchBoxModelObserverBridge(AppsSearchBoxController* parent);
+ virtual ~SearchBoxModelObserverBridge();
+
+ void SetSearchText(const string16& text);
+
+ virtual void IconChanged() OVERRIDE;
+ virtual void HintTextChanged() OVERRIDE;
+ virtual void SelectionModelChanged() OVERRIDE;
+ virtual void TextChanged() OVERRIDE;
+
+ private:
+ SearchBoxModel* GetModel();
+
+ AppsSearchBoxController* parent_; // Weak. Owns us.
+
+ DISALLOW_COPY_AND_ASSIGN(SearchBoxModelObserverBridge);
+};
+
+SearchBoxModelObserverBridge::SearchBoxModelObserverBridge(
+ AppsSearchBoxController* parent)
+ : parent_(parent) {
+ IconChanged();
+ HintTextChanged();
+ GetModel()->AddObserver(this);
+}
+
+SearchBoxModelObserverBridge::~SearchBoxModelObserverBridge() {
+ GetModel()->RemoveObserver(this);
+}
+
+SearchBoxModel* SearchBoxModelObserverBridge::GetModel() {
+ SearchBoxModel* searchBoxModel = [[parent_ delegate] searchBoxModel];
+ DCHECK(searchBoxModel);
+ return searchBoxModel;
+}
+
+void SearchBoxModelObserverBridge::SetSearchText(const string16& text) {
+ SearchBoxModel* model = GetModel();
+ model->RemoveObserver(this);
+ model->SetText(text);
+ // TODO(tapted): See if this should call SetSelectionModel here.
+ model->AddObserver(this);
+}
+
+void SearchBoxModelObserverBridge::IconChanged() {
+ [[parent_ searchImageView]
+ setImage:gfx::NSImageFromImageSkia(GetModel()->icon())];
+}
+
+void SearchBoxModelObserverBridge::HintTextChanged() {
+ [[[parent_ searchTextField] cell] setPlaceholderString:
+ base::SysUTF16ToNSString(GetModel()->hint_text())];
+}
+
+void SearchBoxModelObserverBridge::SelectionModelChanged() {
+ // TODO(tapted): See if anything needs to be done here for RTL.
+}
+
+void SearchBoxModelObserverBridge::TextChanged() {
+ // Currently the model text is only changed when we are not observing it, or
+ // it is changed in tests to establish a particular state.
+ [[parent_ searchTextField]
+ setStringValue:base::SysUTF16ToNSString(GetModel()->text())];
+}
+
+} // namespace app_list
+
+@interface SearchTextField : NSTextField {
+ @private
+ NSRect textFrameInset_;
+}
+
+@property(readonly, nonatomic) NSRect textFrameInset;
+
+- (void)setMarginsWithLeftMargin:(CGFloat)leftMargin
+ rightMargin:(CGFloat)rightMargin;
+
+@end
+
+@implementation AppsSearchBoxController
+
+@synthesize delegate = delegate_;
+
+- (id)initWithFrame:(NSRect)frame {
+ if ((self = [super init])) {
+ scoped_nsobject<NSView> containerView([[NSView alloc] initWithFrame:frame]);
+ [self setView:containerView];
+ [self addSubviews];
+ }
+ return self;
+}
+
+- (void)clearSearch {
+ [searchTextField_ setStringValue:@""];
+ [self controlTextDidChange:nil];
+}
+
+- (void)setDelegate:(id<AppsSearchBoxDelegate>)delegate {
+ bridge_.reset(); // Ensure observers are cleared before updating |delegate_|.
+ delegate_ = delegate;
+ if (!delegate_)
+ return;
+
+ bridge_.reset(new app_list::SearchBoxModelObserverBridge(self));
+}
+
+- (NSTextField*)searchTextField {
+ return searchTextField_;
+}
+
+- (NSImageView*)searchImageView {
+ return searchImageView_;
+}
+
+- (void)addSubviews {
+ NSRect viewBounds = [[self view] bounds];
+ searchImageView_.reset([[NSImageView alloc] initWithFrame:NSMakeRect(
+ kPadding, 0, kSearchIconDimension, NSHeight(viewBounds))]);
+
+ searchTextField_.reset([[SearchTextField alloc] initWithFrame:viewBounds]);
+ [searchTextField_ setDelegate:self];
+ [searchTextField_ setFont:ui::ResourceBundle::GetSharedInstance().GetFont(
+ ui::ResourceBundle::MediumFont).GetNativeFont()];
+ [searchTextField_
+ setMarginsWithLeftMargin:NSMaxX([searchImageView_ frame]) + kPadding
+ rightMargin:kPadding];
+
+ [[self view] addSubview:searchImageView_];
+ [[self view] addSubview:searchTextField_];
+}
+
+- (BOOL)control:(NSControl*)control
+ textView:(NSTextView*)textView
+ doCommandBySelector:(SEL)command {
+ // Forward the message first, to handle grid or search results navigation.
+ BOOL handled = [delegate_ control:control
+ textView:textView
+ doCommandBySelector:command];
+ if (handled)
+ return YES;
+
+ // If the delegate did not handle the escape key, it means the window was not
+ // dismissed because there were search results. Clear them.
+ if (command == @selector(complete:)) {
+ [self clearSearch];
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)controlTextDidChange:(NSNotification*)notification {
+ if (bridge_) {
+ bridge_->SetSearchText(
+ base::SysNSStringToUTF16([searchTextField_ stringValue]));
+ }
+
+ [delegate_ modelTextDidChange];
+}
+
+@end
+
+@interface SearchTextFieldCell : NSTextFieldCell;
+
+- (NSRect)textFrameForFrameInternal:(NSRect)cellFrame;
+
+@end
+
+@implementation SearchTextField
+
+@synthesize textFrameInset = textFrameInset_;
+
++ (Class)cellClass {
+ return [SearchTextFieldCell class];
+}
+
+- (id)initWithFrame:(NSRect)theFrame {
+ if ((self = [super initWithFrame:theFrame])) {
+ [self setFocusRingType:NSFocusRingTypeNone];
+ [self setDrawsBackground:NO];
+ [self setBordered:NO];
+ }
+ return self;
+}
+
+- (void)setMarginsWithLeftMargin:(CGFloat)leftMargin
+ rightMargin:(CGFloat)rightMargin {
+ // Find the preferred height for the current text properties, and center.
+ NSRect viewBounds = [self bounds];
+ [self sizeToFit];
+ NSRect textBounds = [self bounds];
+ textFrameInset_.origin.x = leftMargin;
+ textFrameInset_.origin.y = floor(NSMidY(viewBounds) - NSMidY(textBounds));
+ textFrameInset_.size.width = leftMargin + rightMargin;
+ textFrameInset_.size.height = NSHeight(viewBounds) - NSHeight(textBounds);
+ [self setFrame:viewBounds];
+}
+
+@end
+
+@implementation SearchTextFieldCell
+
+- (NSRect)textFrameForFrameInternal:(NSRect)cellFrame {
+ SearchTextField* searchTextField =
+ base::mac::ObjCCastStrict<SearchTextField>([self controlView]);
+ NSRect insetRect = [searchTextField textFrameInset];
+ cellFrame.origin.x += insetRect.origin.x;
+ cellFrame.origin.y += insetRect.origin.y;
+ cellFrame.size.width -= insetRect.size.width;
+ cellFrame.size.height -= insetRect.size.height;
+ return cellFrame;
+}
+
+- (NSRect)textFrameForFrame:(NSRect)cellFrame {
+ return [self textFrameForFrameInternal:cellFrame];
+}
+
+- (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
+ return [self textFrameForFrameInternal:cellFrame];
+}
+
+- (void)resetCursorRect:(NSRect)cellFrame
+ inView:(NSView*)controlView {
+ [super resetCursorRect:[self textCursorFrameForFrame:cellFrame]
+ inView:controlView];
+}
+
+- (NSRect)drawingRectForBounds:(NSRect)theRect {
+ return [super drawingRectForBounds:[self textFrameForFrame:theRect]];
+}
+
+- (void)editWithFrame:(NSRect)cellFrame
+ inView:(NSView*)controlView
+ editor:(NSText*)editor
+ delegate:(id)delegate
+ event:(NSEvent*)event {
+ [super editWithFrame:[self textFrameForFrame:cellFrame]
+ inView:controlView
+ editor:editor
+ delegate:delegate
+ event:event];
+}
+
+- (void)selectWithFrame:(NSRect)cellFrame
+ inView:(NSView*)controlView
+ editor:(NSText*)editor
+ delegate:(id)delegate
+ start:(NSInteger)start
+ length:(NSInteger)length {
+ [super selectWithFrame:[self textFrameForFrame:cellFrame]
+ inView:controlView
+ editor:editor
+ delegate:delegate
+ start:start
+ length:length];
+}
+
+@end
diff --git a/ui/app_list/cocoa/apps_search_box_controller_unittest.mm b/ui/app_list/cocoa/apps_search_box_controller_unittest.mm
new file mode 100644
index 0000000..ee47687
--- /dev/null
+++ b/ui/app_list/cocoa/apps_search_box_controller_unittest.mm
@@ -0,0 +1,123 @@
+// 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.
+
+#import "ui/app_list/cocoa/apps_search_box_controller.h"
+
+#include "base/memory/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#import "testing/gtest_mac.h"
+#include "ui/app_list/search_box_model.h"
+#import "ui/base/test/ui_cocoa_test_helper.h"
+
+@interface TestAppsSearchBoxDelegate : NSObject<AppsSearchBoxDelegate> {
+ @private
+ app_list::SearchBoxModel searchBoxModel_;
+ int textChangeCount_;
+}
+
+@property(assign, nonatomic) int textChangeCount;
+
+@end
+
+@implementation TestAppsSearchBoxDelegate
+
+@synthesize textChangeCount = textChangeCount_;
+
+- (app_list::SearchBoxModel*)searchBoxModel {
+ return &searchBoxModel_;
+}
+
+- (BOOL)control:(NSControl*)control
+ textView:(NSTextView*)textView
+ doCommandBySelector:(SEL)command {
+ return NO;
+}
+
+- (void)modelTextDidChange {
+ ++textChangeCount_;
+}
+
+- (CGFloat)bubbleCornerRadius {
+ return 3;
+}
+
+@end
+
+namespace app_list {
+namespace test {
+
+class AppsSearchBoxControllerTest : public ui::CocoaTest {
+ public:
+ AppsSearchBoxControllerTest() {
+ Init();
+ }
+
+ virtual void SetUp() OVERRIDE {
+ apps_search_box_controller_.reset(
+ [[AppsSearchBoxController alloc] initWithFrame:
+ NSMakeRect(0, 0, 400, 100)]);
+ delegate_.reset([[TestAppsSearchBoxDelegate alloc] init]);
+ [apps_search_box_controller_ setDelegate:delegate_];
+
+ ui::CocoaTest::SetUp();
+ [[test_window() contentView] addSubview:[apps_search_box_controller_ view]];
+ }
+
+ virtual void TearDown() OVERRIDE {
+ [apps_search_box_controller_ setDelegate:nil];
+ ui::CocoaTest::TearDown();
+ }
+
+ void SimulateKeyAction(SEL c) {
+ NSControl* control = [apps_search_box_controller_ searchTextField];
+ [apps_search_box_controller_ control:control
+ textView:nil
+ doCommandBySelector:c];
+ }
+
+ protected:
+ scoped_nsobject<TestAppsSearchBoxDelegate> delegate_;
+ scoped_nsobject<AppsSearchBoxController> apps_search_box_controller_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AppsSearchBoxControllerTest);
+};
+
+TEST_VIEW(AppsSearchBoxControllerTest, [apps_search_box_controller_ view]);
+
+// Test the search box initialization, and search input and clearing.
+TEST_F(AppsSearchBoxControllerTest, SearchBoxModel) {
+ app_list::SearchBoxModel* model = [delegate_ searchBoxModel];
+ const string16 hit_text(ASCIIToUTF16("hint")); // Usually localized "Search".
+ model->SetHintText(hit_text);
+ EXPECT_NSEQ(base::SysUTF16ToNSString(hit_text),
+ [[[apps_search_box_controller_ searchTextField] cell] placeholderString]);
+
+ const string16 search_text(ASCIIToUTF16("test"));
+ model->SetText(search_text);
+ EXPECT_NSEQ(base::SysUTF16ToNSString(search_text),
+ [[apps_search_box_controller_ searchTextField] stringValue]);
+ // Updates coming via the model should not notify the delegate.
+ EXPECT_EQ(0, [delegate_ textChangeCount]);
+
+ // Updates from the view should update the model and notify the delegate.
+ [apps_search_box_controller_ clearSearch];
+ EXPECT_EQ(string16(), model->text());
+ EXPECT_NSEQ([NSString string],
+ [[apps_search_box_controller_ searchTextField] stringValue]);
+ EXPECT_EQ(1, [delegate_ textChangeCount]);
+
+ // Test pressing escape clears the search.
+ model->SetText(search_text);
+ EXPECT_NSEQ(base::SysUTF16ToNSString(search_text),
+ [[apps_search_box_controller_ searchTextField] stringValue]);
+ SimulateKeyAction(@selector(complete:));
+ EXPECT_NSEQ([NSString string],
+ [[apps_search_box_controller_ searchTextField] stringValue]);
+ EXPECT_EQ(2, [delegate_ textChangeCount]);
+}
+
+} // namespace test
+} // namespace app_list
diff --git a/ui/app_list/cocoa/test/apps_grid_controller_test_helper.h b/ui/app_list/cocoa/test/apps_grid_controller_test_helper.h
index 90a1014..026a4cd 100644
--- a/ui/app_list/cocoa/test/apps_grid_controller_test_helper.h
+++ b/ui/app_list/cocoa/test/apps_grid_controller_test_helper.h
@@ -12,6 +12,9 @@
@class AppsGridController;
namespace app_list {
+
+class AppListModel;
+
namespace test {
class AppListTestViewDelegate;
@@ -55,10 +58,10 @@ class AppsGridControllerTestHelper : public ui::CocoaTest {
NSCollectionView* GetPageAt(size_t index);
NSView* GetSelectedView();
- AppListTestViewDelegate* delegate();
AppListTestModel* model();
- scoped_ptr<AppListTestViewDelegate> delegate_;
+ virtual void ResetModel(scoped_ptr<AppListModel> model);
+
AppsGridController* apps_grid_controller_;
private:
diff --git a/ui/app_list/cocoa/test/apps_grid_controller_test_helper.mm b/ui/app_list/cocoa/test/apps_grid_controller_test_helper.mm
index 1fb663b..9023c5f 100644
--- a/ui/app_list/cocoa/test/apps_grid_controller_test_helper.mm
+++ b/ui/app_list/cocoa/test/apps_grid_controller_test_helper.mm
@@ -20,7 +20,6 @@ const size_t AppsGridControllerTestHelper::kItemsPerPage = 16;
AppsGridControllerTestHelper::AppsGridControllerTestHelper() {
Init();
- delegate_.reset(new AppListTestViewDelegate);
[AppsGridController setScrollAnimationDuration:0.0];
}
@@ -30,8 +29,7 @@ void AppsGridControllerTestHelper::SetUpWithGridController(
AppsGridController* grid_controller) {
ui::CocoaTest::SetUp();
apps_grid_controller_ = grid_controller;
- scoped_ptr<AppListModel> model(new AppListTestModel);
- [apps_grid_controller_ setModel:model.Pass()];
+ ReplaceTestModel(0);
}
void AppsGridControllerTestHelper::SimulateClick(NSView* view) {
@@ -58,7 +56,12 @@ void AppsGridControllerTestHelper::SimulateMouseExitItemAt(size_t index) {
void AppsGridControllerTestHelper::ReplaceTestModel(int item_count) {
scoped_ptr<AppListTestModel> new_model(new AppListTestModel);
new_model->PopulateApps(item_count);
- [apps_grid_controller_ setModel:new_model.PassAs<AppListModel>()];
+ ResetModel(new_model.PassAs<AppListModel>());
+}
+
+void AppsGridControllerTestHelper::ResetModel(
+ scoped_ptr<AppListModel> new_model) {
+ [apps_grid_controller_ setModel:new_model.Pass()];
}
std::string AppsGridControllerTestHelper::GetViewContent() const {
@@ -126,10 +129,6 @@ NSView* AppsGridControllerTestHelper::GetSelectedView() {
return GetItemViewAt([apps_grid_controller_ selectedItemIndex]);
}
-AppListTestViewDelegate* AppsGridControllerTestHelper::delegate() {
- return static_cast<AppListTestViewDelegate*>(delegate_.get());
-}
-
AppListTestModel* AppsGridControllerTestHelper::model() {
return static_cast<AppListTestModel*>([apps_grid_controller_ model]);
}
diff --git a/ui/app_list/views/app_list_background.cc b/ui/app_list/views/app_list_background.cc
index 9cff9cc..cc55b5b 100644
--- a/ui/app_list/views/app_list_background.cc
+++ b/ui/app_list/views/app_list_background.cc
@@ -16,10 +16,7 @@
namespace {
-const SkColor kSearchBoxBackground = SK_ColorWHITE;
-
-// Colors and sizes of top separator between searchbox and grid view.
-const SkColor kTopSeparatorColor = SkColorSetRGB(0xE5, 0xE5, 0xE5);
+// Size of top separator between searchbox and grid view.
const int kTopSeparatorSize = 1;
} // namespace