From c116c70bfaa743e3175f84884d8083c3bf841b70 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 24 Feb 2017 18:15:31 +0200 Subject: [PATCH] Better GUI for user errors/warnings in Cocoa --- Cocoa/Document.m | 79 +++++++++++++++++++++++++++++----------- Cocoa/GBWarningPopover.h | 8 ++++ Cocoa/GBWarningPopover.m | 46 +++++++++++++++++++++++ Cocoa/PopoverView.xib | 27 ++++++++++++++ Makefile | 4 +- 5 files changed, 140 insertions(+), 24 deletions(-) create mode 100644 Cocoa/GBWarningPopover.h create mode 100644 Cocoa/GBWarningPopover.m create mode 100644 Cocoa/PopoverView.xib diff --git a/Cocoa/Document.m b/Cocoa/Document.m index 7d3781e..d3f6e1e 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -10,6 +10,7 @@ #include "display.h" #include "HexFiend/HexFiend.h" #include "GBMemoryByteArray.h" +#include "GBWarningPopover.h" /* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */ /* Todo: Split into category files! This is so messy!!! */ @@ -40,6 +41,10 @@ NSMutableData *currentPrinterImageData; enum {GBAccessoryNone, GBAccessoryPrinter} accessory; + + bool rom_warning_issued; + + NSMutableString *capturedOutput; } @property GBAudioClient *audioClient; @@ -146,7 +151,13 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, GB_set_rgb_encode_callback(&gb, rgbEncode); GB_set_camera_get_pixel_callback(&gb, cameraGetPixel); GB_set_camera_update_request_callback(&gb, cameraRequestUpdate); - [self loadROM]; + NSString *rom_warnings = [self captureOutputForBlock:^{ + [self loadROM]; + }]; + if (rom_warnings && !rom_warning_issued) { + rom_warning_issued = true; + [GBWarningPopover popoverWithContents:rom_warnings onWindow:self.mainWindow]; + } } - (void) vblank @@ -483,6 +494,12 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, - (void) log: (const char *) string withAttributes: (GB_log_attributes) attributes { + NSString *nsstring = @(string); // For ref-counting + if (capturedOutput) { + [capturedOutput appendString:nsstring]; + return; + } + if (pendingLogLines > 128) { /* The ROM causes so many errors in such a short time, and we can't handle it. */ tooMuchLogs = true; @@ -493,7 +510,6 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, /* Make sure mouse is not hidden while debugging */ self.view.mouseHidingEnabled = NO; - NSString *nsstring = @(string); // For ref-counting dispatch_async(dispatch_get_main_queue(), ^{ [hex_controller reloadData]; [self reloadVRAMData: nil]; @@ -585,25 +601,30 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, - (IBAction)saveState:(id)sender { - bool was_running = running; - if (!gb.debug_stopped) { - [self stop]; - } - GB_save_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]); - if (was_running) { - [self start]; + bool __block success = false; + [self performAtomicBlock:^{ + success = GB_save_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]) == 0; + }]; + + if (!success) { + [GBWarningPopover popoverWithContents:@"Failed to write save state." onWindow:self.mainWindow]; + NSBeep(); } } - (IBAction)loadState:(id)sender { - bool was_running = running; - if (!gb.debug_stopped) { - [self stop]; - } - GB_load_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]); - if (was_running) { - [self start]; + bool __block success = false; + NSString *error = + [self captureOutputForBlock:^{ + success = GB_load_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]) == 0; + }]; + + if (!success) { + if (error) { + [GBWarningPopover popoverWithContents:error onWindow:self.mainWindow]; + } + NSBeep(); } } @@ -642,6 +663,15 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, } } +- (NSString *) captureOutputForBlock: (void (^)())block +{ + capturedOutput = [[NSMutableString alloc] init]; + [self performAtomicBlock:block]; + NSString *ret = [capturedOutput stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + capturedOutput = nil; + return [ret length]? ret : nil; +} + + (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale { CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef) data); @@ -754,29 +784,31 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, - (IBAction)hexGoTo:(id)sender { - [self performAtomicBlock:^{ + NSString *error = [self captureOutputForBlock:^{ uint16_t addr; if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, NULL)) { - NSBeep(); return; } addr -= lineRep.valueOffset; if (addr >= hex_controller.byteArray.length) { - NSBeep(); + GB_log(&gb, "Value $%04x is out of range.\n", addr); return; } [hex_controller setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(addr, 0)]]]; [hex_controller _ensureVisibilityOfLocation:addr]; [self.memoryWindow makeFirstResponder:self.memoryView.subviews[0].subviews[0]]; }]; + if (error) { + NSBeep(); + [GBWarningPopover popoverWithContents:error onView:sender]; + } } - (IBAction)hexUpdateBank:(NSControl *)sender { - [self performAtomicBlock:^{ + NSString *error = [self captureOutputForBlock:^{ uint16_t addr, bank; if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, &bank)) { - NSBeep(); return; } @@ -808,6 +840,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height, [(GBMemoryByteArray *)(hex_controller.byteArray) setSelectedBank:bank]; [hex_controller reloadData]; }]; + + if (error) { + NSBeep(); + [GBWarningPopover popoverWithContents:error onView:sender]; + } } - (IBAction)hexUpdateSpace:(NSPopUpButtonCell *)sender diff --git a/Cocoa/GBWarningPopover.h b/Cocoa/GBWarningPopover.h new file mode 100644 index 0000000..1d695b1 --- /dev/null +++ b/Cocoa/GBWarningPopover.h @@ -0,0 +1,8 @@ +#import + +@interface GBWarningPopover : NSPopover + ++ (GBWarningPopover *) popoverWithContents:(NSString *)contents onView:(NSView *)view; ++ (GBWarningPopover *) popoverWithContents:(NSString *)contents onWindow:(NSWindow *)window; + +@end diff --git a/Cocoa/GBWarningPopover.m b/Cocoa/GBWarningPopover.m new file mode 100644 index 0000000..411e388 --- /dev/null +++ b/Cocoa/GBWarningPopover.m @@ -0,0 +1,46 @@ +#import "GBWarningPopover.h" + +static GBWarningPopover *lastPopover; + +@implementation GBWarningPopover + ++ (GBWarningPopover *) popoverWithContents:(NSString *)contents onView:(NSView *)view +{ + [lastPopover close]; + lastPopover = [[self alloc] init]; + + [lastPopover setBehavior:NSPopoverBehaviorApplicationDefined]; + [lastPopover setAnimates:YES]; + lastPopover.contentViewController = [[NSViewController alloc] initWithNibName:@"PopoverView" bundle:nil]; + NSTextField *field = (NSTextField *)lastPopover.contentViewController.view; + [field setStringValue:contents]; + NSSize textSize = [field.cell cellSizeForBounds:[field.cell drawingRectForBounds:NSMakeRect(0, 0, 240, CGFLOAT_MAX)]]; + textSize.width = ceil(textSize.width) + 16; + textSize.height = ceil(textSize.height) + 12; + [lastPopover setContentSize:textSize]; + + if (!view.window.isVisible) { + [view.window setIsVisible:YES]; + } + + [lastPopover showRelativeToRect:view.bounds + ofView:view + preferredEdge:NSMinYEdge]; + + NSRect frame = field.frame; + frame.origin.x += 8; + frame.origin.y -= 6; + field.frame = frame; + + + [lastPopover performSelector:@selector(close) withObject:nil afterDelay:3.0]; + + return lastPopover; +} + ++ (GBWarningPopover *)popoverWithContents:(NSString *)contents onWindow:(NSWindow *)window +{ + return [self popoverWithContents:contents onView:window.contentView.superview.subviews.lastObject]; +} + +@end diff --git a/Cocoa/PopoverView.xib b/Cocoa/PopoverView.xib new file mode 100644 index 0000000..7ccdf49 --- /dev/null +++ b/Cocoa/PopoverView.xib @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Makefile b/Makefile index f87f325..046e11a 100755 --- a/Makefile +++ b/Makefile @@ -153,9 +153,7 @@ $(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \ Misc/registers.sym \ $(BIN)/SameBoy.app/Contents/Resources/dmg_boot.bin \ $(BIN)/SameBoy.app/Contents/Resources/cgb_boot.bin \ - $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/Document.nib \ - $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/MainMenu.nib \ - $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/Preferences.nib \ + $(patsubst %.xib,%.nib,$(addprefix $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/,$(shell cd Cocoa;ls *.xib))) \ $(BIN)/SameBoy.qlgenerator \ Shaders $(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources