Better GUI for user errors/warnings in Cocoa
This commit is contained in:
parent
724153e5ef
commit
c116c70bfa
@ -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
|
||||
|
8
Cocoa/GBWarningPopover.h
Normal file
8
Cocoa/GBWarningPopover.h
Normal file
@ -0,0 +1,8 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GBWarningPopover : NSPopover
|
||||
|
||||
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents onView:(NSView *)view;
|
||||
+ (GBWarningPopover *) popoverWithContents:(NSString *)contents onWindow:(NSWindow *)window;
|
||||
|
||||
@end
|
46
Cocoa/GBWarningPopover.m
Normal file
46
Cocoa/GBWarningPopover.m
Normal file
@ -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
|
27
Cocoa/PopoverView.xib
Normal file
27
Cocoa/PopoverView.xib
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSViewController">
|
||||
<connections>
|
||||
<outlet property="view" destination="oUc-bq-d5t" id="FQR-Ty-0Ar"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="oUc-bq-d5t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="66" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<textFieldCell key="cell" controlSize="mini" sendsActionOnEndEditing="YES" alignment="left" title="Test" id="xyx-iy-kse">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<point key="canvasLocation" x="-93" y="211.5"/>
|
||||
</textField>
|
||||
</objects>
|
||||
</document>
|
4
Makefile
4
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
|
||||
|
Loading…
Reference in New Issue
Block a user