X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=OSX%2FSaverListController.m;h=9377275b7a51b01724d1e9e5f48d7f942dd16d27;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hp=75e8751ba2b59a0f7529e0e5c873ea0b01c37c37;hpb=c70f94f648d51bb4828193124f325fa52b0e57f3;p=xscreensaver diff --git a/OSX/SaverListController.m b/OSX/SaverListController.m index 75e8751b..9377275b 100644 --- a/OSX/SaverListController.m +++ b/OSX/SaverListController.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2012 Jamie Zawinski +/* xscreensaver, Copyright (c) 2012-2014 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -16,6 +16,7 @@ #import "SaverListController.h" #import "SaverRunner.h" +#import "yarandom.h" #import "version.h" #undef countof @@ -27,7 +28,7 @@ - (void) titleTapped:(id) sender { [[UIApplication sharedApplication] - openURL:[NSURL URLWithString:@"http://www.jwz.org/xscreensaver/"]]; + openURL:[NSURL URLWithString:@"https://www.jwz.org/xscreensaver/"]]; } @@ -35,6 +36,7 @@ { // Extract the version number and release date from the version string. // Here's an area where I kind of wish I had "Two Problems". + // I guess I could add custom key to the Info.plist for this. NSArray *a = [[NSString stringWithCString: screensaver_id encoding:NSASCIIStringEncoding] @@ -56,15 +58,17 @@ UIImage *img = [UIImage imageWithContentsOfFile: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: - @"iSaverRunner29t.png"]]; - UIBarButtonItem *button = [[[UIBarButtonItem alloc] - initWithImage: img - style: UIBarButtonItemStylePlain - target: self - action: @selector(titleTapped:)] - autorelease]; - button.width = img.size.width; - self.navigationItem.rightBarButtonItem = button; + @"iSaverRunner57t.png"]]; + UIButton *button = [[UIButton alloc] init]; + [button setFrame: CGRectMake(0, 0, img.size.width/2, img.size.height/2)]; + [button setBackgroundImage:img forState:UIControlStateNormal]; + [button addTarget:self + action:@selector(titleTapped:) + forControlEvents:UIControlEventTouchUpInside]; + UIBarButtonItem *bi = [[UIBarButtonItem alloc] initWithCustomView: button]; + self.navigationItem.rightBarButtonItem = bi; + [bi release]; + [button release]; // The title bar @@ -85,9 +89,9 @@ CGRect r3 = r2; CGRect win = [self view].frame; - if (win.size.width > 320) { // iPad - [label1 setTextAlignment: UITextAlignmentLeft]; - [label2 setTextAlignment: UITextAlignmentRight]; + if (win.size.width > 414 && win.size.height > 414) { // iPad + [label1 setTextAlignment: NSTextAlignmentLeft]; + [label2 setTextAlignment: NSTextAlignmentRight]; label2.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; r3.size.width = win.size.width; r1 = r3; @@ -96,9 +100,9 @@ r2 = r1; } else { // iPhone - r3.size.width = 320; // force it to be flush-left - [label1 setTextAlignment: UITextAlignmentLeft]; - [label2 setTextAlignment: UITextAlignmentLeft]; + r3.size.width = win.size.width; // force it to be flush-left + [label1 setTextAlignment: NSTextAlignmentLeft]; + [label2 setTextAlignment: NSTextAlignmentLeft]; r1.origin.y = -1; // make it fit in landscape r2.origin.y = r1.origin.y + r1.size.height - 2; r3.size.height = r1.size.height + r2.size.height; @@ -111,15 +115,33 @@ [v addSubview:label1]; [v addSubview:label2]; + // Default opacity looks bad. + [v setBackgroundColor:[[v backgroundColor] colorWithAlphaComponent:1]]; + self.navigationItem.titleView = v; + + win.origin.x = 0; + win.origin.y = 0; + win.size.height = 44; // #### This cannot possibly be right. + UISearchBar *search = [[UISearchBar alloc] initWithFrame:win]; + search.delegate = self; + search.placeholder = @"Search..."; + self.tableView.tableHeaderView = search; + + // Dismiss the search field's keyboard as soon as we scroll. +# ifdef __IPHONE_7_0 + if ([self.tableView respondsToSelector:@selector(keyboardDismissMode)]) + [self.tableView setKeyboardDismissMode: + UIScrollViewKeyboardDismissModeOnDrag]; +# endif } -- (id)initWithNames:(NSArray *)names descriptions:(NSDictionary *)descs; +- (id)initWithNames:(NSArray *)_names descriptions:(NSDictionary *)_descs; { self = [self init]; if (! self) return 0; - [self reload:names descriptions:descs]; + [self reload:_names descriptions:_descs search:nil]; [self makeTitleBar]; return self; } @@ -130,6 +152,8 @@ int n = countof(list_by_letter); NSMutableArray *a = [NSMutableArray arrayWithCapacity: n]; for (int i = 0; i < n; i++) { + if ([list_by_letter[i] count] == 0) // Omit empty letter sections. + continue; char s[2]; s[0] = (i == 'Z'-'A'+1 ? '#' : i+'A'); s[1] = 0; @@ -140,11 +164,25 @@ } -- (void) reload:(NSArray *)names descriptions:(NSDictionary *)descs +/* Called when text is typed into the top search bar. + */ +- (void)searchBar:(UISearchBar *)bar textDidChange:(NSString *)txt { - if (descriptions) - [descriptions release]; - descriptions = [descs retain]; + [self reload:names descriptions:descriptions search:txt]; +} + + +- (void) reload:(NSArray *)_names descriptions:(NSDictionary *)_descs + search:search +{ + if (names != _names) { + if (names) [names release]; + names = [_names retain]; + } + if (_descs != descriptions) { + if (descriptions) [descriptions release]; + descriptions = [_descs retain]; + } int n = countof(list_by_letter); for (int i = 0; i < n; i++) { @@ -152,6 +190,25 @@ } for (NSString *name in names) { + + // If we're searching, omit any items that don't have a match in the + // title or description. + // + BOOL matchp = (!search || [search length] == 0); + if (! matchp) { + matchp = ([name rangeOfString:search + options:NSCaseInsensitiveSearch].location + != NSNotFound); + } + if (! matchp) { + NSString *desc = [descriptions objectForKey:name]; + matchp = ([desc rangeOfString:search + options:NSCaseInsensitiveSearch].location + != NSNotFound); + } + if (! matchp) + continue; + int index = ([name cStringUsingEncoding: NSASCIIStringEncoding])[0]; if (index >= 'a' && index <= 'z') index -= 'a'-'A'; @@ -214,25 +271,23 @@ - (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)ip { - NSString *id = + NSString *title = [[letter_sections objectAtIndex: [ip indexAtPosition: 0]] objectAtIndex: [ip indexAtPosition: 1]]; - NSString *desc = [descriptions objectForKey:id]; + NSString *desc = [descriptions objectForKey:title]; - UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier: id]; - if (!cell) { + NSString *id = @"Cell"; + UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:id]; + if (!cell) cell = [[[UITableViewCell alloc] - initWithStyle: (desc - ? UITableViewCellStyleSubtitle - : UITableViewCellStyleDefault) + initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: id] autorelease]; - cell.textLabel.text = id; - cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; - if (desc) - cell.detailTextLabel.text = desc; - } + cell.textLabel.text = title; + cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; + cell.detailTextLabel.text = desc; + return cell; } @@ -246,8 +301,11 @@ SaverRunner *s = (SaverRunner *) [[UIApplication sharedApplication] delegate]; if (! s) return; - if (! [s isKindOfClass:[SaverRunner class]]) - abort(); + + // Dismiss the search field's keyboard before launching a saver. + [self.tableView.tableHeaderView resignFirstResponder]; + + NSAssert ([s isKindOfClass:[SaverRunner class]], @"not a SaverRunner"); [s loadSaver: cell.textLabel.text]; } @@ -260,8 +318,7 @@ SaverRunner *s = (SaverRunner *) [[UIApplication sharedApplication] delegate]; if (! s) return; - if (! [s isKindOfClass:[SaverRunner class]]) - abort(); + NSAssert ([s isKindOfClass:[SaverRunner class]], @"not a SaverRunner"); [s openPreferences: cell.textLabel.text]; } @@ -290,11 +347,44 @@ } -- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)o +/* We need this to respond to "shake" gestures + */ +- (BOOL)canBecomeFirstResponder { return YES; } +- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event +{ +} + +- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event +{ +} + + +/* Shake means load a random screen saver. + */ +- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event +{ + if (motion != UIEventSubtypeMotionShake) + return; + NSMutableArray *a = [NSMutableArray arrayWithCapacity: 200]; + for (NSArray *sec in letter_sections) + for (NSString *s in sec) + [a addObject: s]; + int n = [a count]; + if (! n) return; + NSString *which = [a objectAtIndex: (random() % n)]; + + SaverRunner *s = + (SaverRunner *) [[UIApplication sharedApplication] delegate]; + if (! s) return; + NSAssert ([s isKindOfClass:[SaverRunner class]], @"not a SaverRunner"); + [self scrollTo: which]; + [s loadSaver: which]; +} + - (void)dealloc {