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 "display.h"
|
||||||
#include "HexFiend/HexFiend.h"
|
#include "HexFiend/HexFiend.h"
|
||||||
#include "GBMemoryByteArray.h"
|
#include "GBMemoryByteArray.h"
|
||||||
|
#include "GBWarningPopover.h"
|
||||||
|
|
||||||
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
|
/* 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!!! */
|
/* Todo: Split into category files! This is so messy!!! */
|
||||||
@ -40,6 +41,10 @@
|
|||||||
|
|
||||||
NSMutableData *currentPrinterImageData;
|
NSMutableData *currentPrinterImageData;
|
||||||
enum {GBAccessoryNone, GBAccessoryPrinter} accessory;
|
enum {GBAccessoryNone, GBAccessoryPrinter} accessory;
|
||||||
|
|
||||||
|
bool rom_warning_issued;
|
||||||
|
|
||||||
|
NSMutableString *capturedOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property GBAudioClient *audioClient;
|
@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_rgb_encode_callback(&gb, rgbEncode);
|
||||||
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
|
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
|
||||||
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
||||||
|
NSString *rom_warnings = [self captureOutputForBlock:^{
|
||||||
[self loadROM];
|
[self loadROM];
|
||||||
|
}];
|
||||||
|
if (rom_warnings && !rom_warning_issued) {
|
||||||
|
rom_warning_issued = true;
|
||||||
|
[GBWarningPopover popoverWithContents:rom_warnings onWindow:self.mainWindow];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) vblank
|
- (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
|
- (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) {
|
if (pendingLogLines > 128) {
|
||||||
/* The ROM causes so many errors in such a short time, and we can't handle it. */
|
/* The ROM causes so many errors in such a short time, and we can't handle it. */
|
||||||
tooMuchLogs = true;
|
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 */
|
/* Make sure mouse is not hidden while debugging */
|
||||||
self.view.mouseHidingEnabled = NO;
|
self.view.mouseHidingEnabled = NO;
|
||||||
|
|
||||||
NSString *nsstring = @(string); // For ref-counting
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[hex_controller reloadData];
|
[hex_controller reloadData];
|
||||||
[self reloadVRAMData: nil];
|
[self reloadVRAMData: nil];
|
||||||
@ -585,25 +601,30 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
|
|
||||||
- (IBAction)saveState:(id)sender
|
- (IBAction)saveState:(id)sender
|
||||||
{
|
{
|
||||||
bool was_running = running;
|
bool __block success = false;
|
||||||
if (!gb.debug_stopped) {
|
[self performAtomicBlock:^{
|
||||||
[self stop];
|
success = GB_save_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]) == 0;
|
||||||
}
|
}];
|
||||||
GB_save_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]);
|
|
||||||
if (was_running) {
|
if (!success) {
|
||||||
[self start];
|
[GBWarningPopover popoverWithContents:@"Failed to write save state." onWindow:self.mainWindow];
|
||||||
|
NSBeep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)loadState:(id)sender
|
- (IBAction)loadState:(id)sender
|
||||||
{
|
{
|
||||||
bool was_running = running;
|
bool __block success = false;
|
||||||
if (!gb.debug_stopped) {
|
NSString *error =
|
||||||
[self stop];
|
[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];
|
||||||
}
|
}
|
||||||
GB_load_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]);
|
NSBeep();
|
||||||
if (was_running) {
|
|
||||||
[self start];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
+ (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale
|
||||||
{
|
{
|
||||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef) data);
|
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
|
- (IBAction)hexGoTo:(id)sender
|
||||||
{
|
{
|
||||||
[self performAtomicBlock:^{
|
NSString *error = [self captureOutputForBlock:^{
|
||||||
uint16_t addr;
|
uint16_t addr;
|
||||||
if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, NULL)) {
|
if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, NULL)) {
|
||||||
NSBeep();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addr -= lineRep.valueOffset;
|
addr -= lineRep.valueOffset;
|
||||||
if (addr >= hex_controller.byteArray.length) {
|
if (addr >= hex_controller.byteArray.length) {
|
||||||
NSBeep();
|
GB_log(&gb, "Value $%04x is out of range.\n", addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[hex_controller setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(addr, 0)]]];
|
[hex_controller setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(addr, 0)]]];
|
||||||
[hex_controller _ensureVisibilityOfLocation:addr];
|
[hex_controller _ensureVisibilityOfLocation:addr];
|
||||||
[self.memoryWindow makeFirstResponder:self.memoryView.subviews[0].subviews[0]];
|
[self.memoryWindow makeFirstResponder:self.memoryView.subviews[0].subviews[0]];
|
||||||
}];
|
}];
|
||||||
|
if (error) {
|
||||||
|
NSBeep();
|
||||||
|
[GBWarningPopover popoverWithContents:error onView:sender];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)hexUpdateBank:(NSControl *)sender
|
- (IBAction)hexUpdateBank:(NSControl *)sender
|
||||||
{
|
{
|
||||||
[self performAtomicBlock:^{
|
NSString *error = [self captureOutputForBlock:^{
|
||||||
uint16_t addr, bank;
|
uint16_t addr, bank;
|
||||||
if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, &bank)) {
|
if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, &bank)) {
|
||||||
NSBeep();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,6 +840,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
[(GBMemoryByteArray *)(hex_controller.byteArray) setSelectedBank:bank];
|
[(GBMemoryByteArray *)(hex_controller.byteArray) setSelectedBank:bank];
|
||||||
[hex_controller reloadData];
|
[hex_controller reloadData];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
NSBeep();
|
||||||
|
[GBWarningPopover popoverWithContents:error onView:sender];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)hexUpdateSpace:(NSPopUpButtonCell *)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 \
|
Misc/registers.sym \
|
||||||
$(BIN)/SameBoy.app/Contents/Resources/dmg_boot.bin \
|
$(BIN)/SameBoy.app/Contents/Resources/dmg_boot.bin \
|
||||||
$(BIN)/SameBoy.app/Contents/Resources/cgb_boot.bin \
|
$(BIN)/SameBoy.app/Contents/Resources/cgb_boot.bin \
|
||||||
$(BIN)/SameBoy.app/Contents/Resources/Base.lproj/Document.nib \
|
$(patsubst %.xib,%.nib,$(addprefix $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/,$(shell cd Cocoa;ls *.xib))) \
|
||||||
$(BIN)/SameBoy.app/Contents/Resources/Base.lproj/MainMenu.nib \
|
|
||||||
$(BIN)/SameBoy.app/Contents/Resources/Base.lproj/Preferences.nib \
|
|
||||||
$(BIN)/SameBoy.qlgenerator \
|
$(BIN)/SameBoy.qlgenerator \
|
||||||
Shaders
|
Shaders
|
||||||
$(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources
|
$(MKDIR) -p $(BIN)/SameBoy.app/Contents/Resources
|
||||||
|
Loading…
Reference in New Issue
Block a user