diff --git a/PanoPainter-OSX/main.cpp b/PanoPainter-OSX/main.cpp index eb92b29..9e29c22 100644 --- a/PanoPainter-OSX/main.cpp +++ b/PanoPainter-OSX/main.cpp @@ -13,6 +13,32 @@ #include #import "objc_utils.h" +void global_exception_handler(NSException* e) +{ + NSAlert *alert = [NSAlert alertWithMessageText:@"Recovery" + defaultButton:@"OK" alternateButton:@"Cancel" + otherButton:nil informativeTextWithFormat:@"Exception"]; + [alert runModal]; + + if (App::I.canvas && App::I.canvas->m_canvas) + App::I.canvas->m_canvas->project_save_thread(App::I.data_path + "/recovery.pano"); + + std::terminate(); +} + +void global_signal_handler(int e) +{ + NSAlert *alert = [NSAlert alertWithMessageText:@"Recovery" + defaultButton:@"OK" alternateButton:@"Cancel" + otherButton:nil informativeTextWithFormat:@"Signal"]; + [alert runModal]; + + if (App::I.canvas && App::I.canvas->m_canvas) + App::I.canvas->m_canvas->project_save_thread(App::I.data_path + "/recovery.pano"); + + std::terminate(); +} + @implementation View - (void)async_lock { @@ -27,6 +53,10 @@ { CGLFlushDrawable([glctx CGLContextObj]); } +- (void)close +{ + [[NSApplication sharedApplication] terminate:nil]; +} - (std::string)pick_file { NSOpenPanel *panel = [NSOpenPanel openPanel]; @@ -307,32 +337,67 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime CGLLockContext([[self openGLContext] CGLContextObj]); auto keyCode = [theEvent keyCode]; auto chars = [theEvent characters]; - App::I.key_down(convert_key(keyCode)); - if (const char* cstr = [chars cStringUsingEncoding:NSASCIIStringEncoding]) - App::I.key_char(cstr[0]); + if (App::I.keys[(int)kKey::KeyCtrl]) + { + App::I.key_down(convert_key(keyCode)); + App::I.key_up(convert_key(keyCode)); + } + else + { + App::I.key_down(convert_key(keyCode)); + if (const char* cstr = [chars cStringUsingEncoding:NSASCIIStringEncoding]) + App::I.key_char(cstr[0]); + } CGLUnlockContext([[self openGLContext] CGLContextObj]); } - (void)keyUp:(NSEvent *)theEvent { CGLLockContext([[self openGLContext] CGLContextObj]); auto keyCode = [theEvent keyCode]; - auto chars = [theEvent characters]; + //auto chars = [theEvent characters]; App::I.key_up(convert_key(keyCode)); CGLUnlockContext([[self openGLContext] CGLContextObj]); } +- (void)flagsChanged:(NSEvent *)event +{ + [super flagsChanged:event]; + + CGLLockContext([[self openGLContext] CGLContextObj]); + static bool pressed_shift = false; + if (pressed_shift != ([event modifierFlags] & NSShiftKeyMask)) + { + pressed_shift = ([event modifierFlags] & NSShiftKeyMask); + pressed_shift ? App::I.key_down(kKey::KeyShift) : App::I.key_up(kKey::KeyShift); + } + static bool pressed_alt = false; + if (pressed_alt != ([event modifierFlags] & NSAlternateKeyMask)) + { + pressed_alt = ([event modifierFlags] & NSAlternateKeyMask); + pressed_alt ? App::I.key_down(kKey::KeyAlt) : App::I.key_up(kKey::KeyAlt); + } + static bool pressed_cmd = false; + if (pressed_cmd != ([event modifierFlags] & NSCommandKeyMask)) + { + pressed_cmd = ([event modifierFlags] & NSCommandKeyMask); + pressed_cmd ? App::I.key_down(kKey::KeyCtrl) : App::I.key_up(kKey::KeyCtrl); + } + CGLUnlockContext([[self openGLContext] CGLContextObj]); +} @end -@interface Window : NSWindow -@end @implementation Window +@implementation Window @end -@interface Controller : NSWindowController -@end @implementation Controller +@implementation Controller - (void)windowWillClose:(NSNotification *)notification { App::I.terminate(); [[NSApplication sharedApplication] terminate:nil]; } +-(BOOL)windowShouldClose:(NSWindow *)sender +{ + return App::I.request_close(); +} @end @interface AppOSX : NSApplication @@ -356,6 +421,9 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime } - (void)applicationDidFinishLaunching:(NSNotification *)notification { + if (!App::I.check_license()) + return; + App::I.initLog(); App::I.create(); NSRect r = NSMakeRect(0, 0, App::I.width, App::I.height); @@ -373,6 +441,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime [window setContentView:view]; [window setAcceptsMouseMovedEvents:true]; [window makeFirstResponder:view]; + view->wnd = window; auto menubar = [NSMenu new]; auto appMenuItem = [NSMenuItem new]; @@ -394,6 +463,8 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime int main(int argc, const char * argv[]) { AppOSX* app = [AppOSX sharedApplication]; + NSSetUncaughtExceptionHandler(&global_exception_handler); + signal(SIGFPE, global_signal_handler); [app run]; return 0; } diff --git a/PanoPainter-OSX/main.h b/PanoPainter-OSX/main.h index 0abc5ed..babdf1d 100644 --- a/PanoPainter-OSX/main.h +++ b/PanoPainter-OSX/main.h @@ -3,13 +3,21 @@ #include #include +@interface Window : NSWindow +@end + +@interface Controller : NSWindowController +@end + @interface View : NSOpenGLView { + @public Window* wnd; CVDisplayLinkRef dl; NSOpenGLContext* glctx; _CGLContextObject* cgl; bool gl_ready; } +- (void)close; - (void)terminateGL; - (void)async_lock; - (void)async_unlock; diff --git a/PanoPainter/AppDelegate.h b/PanoPainter/AppDelegate.h index 53a5a4a..c7f3efe 100644 --- a/PanoPainter/AppDelegate.h +++ b/PanoPainter/AppDelegate.h @@ -12,6 +12,9 @@ @property (strong, nonatomic) UIWindow *window; +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options; @end diff --git a/PanoPainter/AppDelegate.m b/PanoPainter/AppDelegate.m index 73aac62..fd29181 100644 --- a/PanoPainter/AppDelegate.m +++ b/PanoPainter/AppDelegate.m @@ -10,6 +10,22 @@ #import "GameViewController.h" #include "app.h" +void global_exception_handler(NSException* e) +{ + if (App::I.canvas && App::I.canvas->m_canvas) + App::I.canvas->m_canvas->project_save_thread(App::I.data_path + "/recovery.pano"); + + std::terminate(); +} + +void global_signal_handler(int e) +{ + if (App::I.canvas && App::I.canvas->m_canvas) + App::I.canvas->m_canvas->project_save_thread(App::I.data_path + "/recovery.pano"); + + std::terminate(); +} + @interface AppDelegate () { GameViewController* view; } @@ -18,9 +34,15 @@ @implementation AppDelegate +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options +{ + App::I.canvas->m_canvas->project_open([url fileSystemRepresentation]); + return true; +} - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. + NSSetUncaughtExceptionHandler(&global_exception_handler); view = (GameViewController*)self.window.rootViewController; return YES; } diff --git a/PanoPainter/Info.plist b/PanoPainter/Info.plist index 8f274b3..d842bd6 100644 --- a/PanoPainter/Info.plist +++ b/PanoPainter/Info.plist @@ -2,14 +2,23 @@ - LSSupportsOpeningDocumentsInPlace - - UIFileSharingEnabled - CFBundleDevelopmentRegion en CFBundleDisplayName PanoPainter + CFBundleDocumentTypes + + + CFBundleTypeIconFiles + + CFBundleTypeName + PanoPainter Image + LSItemContentTypes + + com.panopainter.image + + + CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -28,8 +37,12 @@ 1 LSRequiresIPhoneOS + LSSupportsOpeningDocumentsInPlace + NSPhotoLibraryUsageDescription Export the panoramic to the gallery + UIFileSharingEnabled + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -53,5 +66,30 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UTExportedTypeDeclarations + + + UTTypeConformsTo + + public.data + public.content + + UTTypeDescription + PanoPainter Image + UTTypeIconFiles + + UTTypeIdentifier + com.panopainter.image + UTTypeTagSpecification + + public.filename-extension + + pano + + public.mime-type + image/ppi + + + diff --git a/PanoQL/Base.lproj/MainInterface.storyboard b/PanoQL/Base.lproj/MainInterface.storyboard new file mode 100644 index 0000000..5ec1892 --- /dev/null +++ b/PanoQL/Base.lproj/MainInterface.storyboard @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PanoQL/Info.plist b/PanoQL/Info.plist new file mode 100644 index 0000000..22ba03a --- /dev/null +++ b/PanoQL/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + PanoQL + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + QLSupportedContentTypes + + com.panopainter.image + + QLSupportsSearchableItems + + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.quicklook.preview + + + diff --git a/PanoQL/PreviewViewController.h b/PanoQL/PreviewViewController.h new file mode 100644 index 0000000..f3c8fff --- /dev/null +++ b/PanoQL/PreviewViewController.h @@ -0,0 +1,13 @@ +// +// PreviewViewController.h +// PanoQL +// +// Created by Omar Mohamed Ali Mudhir on 15/06/18. +// Copyright © 2018 OmixLab ltd. All rights reserved. +// + +#import + +@interface PreviewViewController : UIViewController + +@end diff --git a/PanoQL/PreviewViewController.m b/PanoQL/PreviewViewController.m new file mode 100644 index 0000000..99d4bcc --- /dev/null +++ b/PanoQL/PreviewViewController.m @@ -0,0 +1,45 @@ +// +// PreviewViewController.m +// PanoQL +// +// Created by Omar Mohamed Ali Mudhir on 15/06/18. +// Copyright © 2018 OmixLab ltd. All rights reserved. +// + +#import "PreviewViewController.h" +#import + +@interface PreviewViewController () + +@end + +@implementation PreviewViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)preparePreviewOfSearchableItemWithIdentifier:(NSString *)identifier queryString:(NSString * _Nullable)queryString completionHandler:(void (^)(NSError * _Nullable))handler { + // Perform any setup necessary in order to prepare the view. + + // Call the completion handler so Quick Look knows that the preview is fully loaded. + // Quick Look will display a loading spinner while the completion handler is not called. + handler (nil); +} + +/* + * Implement this method if you support previewing files. + * Add the supported content types to the QLSupportedContentTypes array in the Info.plist of the extension. + * +- (void)preparePreviewOfFileAtURL:(NSURL *)url completionHandler:(void (^)(NSError * _Nullable))handler { + handler(nil); +} + */ + +@end diff --git a/PanoThumb/Info.plist b/PanoThumb/Info.plist new file mode 100644 index 0000000..4ee796d --- /dev/null +++ b/PanoThumb/Info.plist @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + PanoThumb + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + QLSupportedContentTypes + + com.panopainter.image + + + NSExtensionPointIdentifier + com.apple.quicklook.thumbnail + NSExtensionPrincipalClass + ThumbnailProvider + + + diff --git a/PanoThumb/ThumbnailProvider.h b/PanoThumb/ThumbnailProvider.h new file mode 100644 index 0000000..3c51668 --- /dev/null +++ b/PanoThumb/ThumbnailProvider.h @@ -0,0 +1,13 @@ +// +// ThumbnailProvider.h +// PanoThumb +// +// Created by Omar Mohamed Ali Mudhir on 15/06/18. +// Copyright © 2018 OmixLab ltd. All rights reserved. +// + +#import + +@interface ThumbnailProvider : QLThumbnailProvider + +@end diff --git a/PanoThumb/ThumbnailProvider.m b/PanoThumb/ThumbnailProvider.m new file mode 100644 index 0000000..1fb93ec --- /dev/null +++ b/PanoThumb/ThumbnailProvider.m @@ -0,0 +1,63 @@ +// +// ThumbnailProvider.m +// PanoThumb +// +// Created by Omar Mohamed Ali Mudhir on 15/06/18. +// Copyright © 2018 OmixLab ltd. All rights reserved. +// + +#import "ThumbnailProvider.h" + +@implementation ThumbnailProvider + +- (void)provideThumbnailForFileRequest:(QLFileThumbnailRequest *)request completionHandler:(void (^)(QLThumbnailReply * _Nullable, NSError * _Nullable))handler { + + // There are three ways to provide a thumbnail through a QLThumbnailReply. Only one of them should be used. + /* + + // First way: Draw the thumbnail into the current context, set up with UIKit's coordinate system. + handler([QLThumbnailReply replyWithContextSize:request.maximumSize currentContextDrawingBlock:^BOOL { + // Draw the thumbnail here. + // Return YES if the thumbnail was successfully drawn inside this block. + return YES; + }], nil); +*/ + + // Second way: Draw the thumbnail into a context passed to your block, set up with Core Graphics' coordinate system. + + CGRect r = CGRectMake(0, 0, 128, 128); + + handler([QLThumbnailReply replyWithContextSize:r.size drawingBlock:^BOOL(CGContextRef _Nonnull context) { + // Draw the thumbnail here. + + NSLog(@"thumbnail generated"); + NSURL* u = [request fileURL]; + FILE* fp = fopen([u fileSystemRepresentation], "rb"); + if (fp) + { + int h[3]; + fread(h, sizeof(int), 3, fp); + + //uint8_t* data = (uint8_t*)malloc(h[0]*h[1]*h[2]); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef bmp = CGBitmapContextCreate(NULL, h[0], h[1], 8, h[0]*h[2], colorSpace, kCGImageAlphaPremultipliedLast); + uint8_t* data = CGBitmapContextGetData(bmp); + fread(data, h[0]*h[1]*h[2], 1, fp); + fclose(fp); + + CGImageRef img = CGBitmapContextCreateImage(bmp); + CGRect r2 = CGRectMake(0, 0, 128*request.scale, 128*request.scale); + CGContextDrawImage(context, r2, img); + } + + // Return YES if the thumbnail was successfully drawn inside this block. + return YES; + }], nil); + /* + // Third way: Set an image file URL. + handler([QLThumbnailReply replyWithImageFileURL:[NSBundle.mainBundle URLForResource:@"fileThumbnail" withExtension:@"jpg"]], nil); + + */ +} + +@end diff --git a/data/layout.xml b/data/layout.xml index 46746e0..e645d53 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -50,7 +50,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -112,7 +112,7 @@ - + @@ -123,7 +123,7 @@ - + @@ -136,7 +136,7 @@ - + diff --git a/data/paper.jpg b/data/paper.jpg index 3c558e3..7347674 100644 Binary files a/data/paper.jpg and b/data/paper.jpg differ diff --git a/engine.xcodeproj/project.pbxproj b/engine.xcodeproj/project.pbxproj index 0ed23e2..f28edfb 100644 --- a/engine.xcodeproj/project.pbxproj +++ b/engine.xcodeproj/project.pbxproj @@ -163,6 +163,14 @@ AD744B6E1EBC9EF800B66E30 /* canvas_modes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD744B6C1EBC9EF700B66E30 /* canvas_modes.cpp */; }; AD759B681F2796EA00211963 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD759B671F2796EA00211963 /* OpenGLES.framework */; }; AD759B691F279B3900211963 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD759B661F27964D00211963 /* GLKit.framework */; }; + AD787ADF20D4611100C4712A /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD787ADE20D4611100C4712A /* QuickLook.framework */; }; + AD787AE320D4611100C4712A /* PreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AD787AE220D4611100C4712A /* PreviewViewController.m */; }; + AD787AE620D4611100C4712A /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AD787AE420D4611100C4712A /* MainInterface.storyboard */; }; + AD787AEA20D4611100C4712A /* PanoQL.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = AD787ADD20D4611100C4712A /* PanoQL.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + AD787AF720D4637E00C4712A /* ThumbnailProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = AD787AF620D4637E00C4712A /* ThumbnailProvider.m */; }; + AD787AFB20D4637E00C4712A /* PanoThumb.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = AD787AF320D4637E00C4712A /* PanoThumb.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + AD787AFF20D5AD1500C4712A /* ZipArchive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD06989620CC6C350010825F /* ZipArchive.framework */; }; + AD787B0020D5AD1700C4712A /* ZipArchive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD06989620CC6C350010825F /* ZipArchive.framework */; }; AD8CF7211E913F0500083FFD /* log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD8CF71F1E913F0500083FFD /* log.cpp */; }; AD95AEC61E41EDEC002DD03A /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD95AEC31E41EDEC002DD03A /* font.cpp */; }; AD95AEC71E41EDEC002DD03A /* pch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD95AEC51E41EDEC002DD03A /* pch.cpp */; }; @@ -240,6 +248,23 @@ ADEBA9092069A50E0085AE16 /* objc_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADEBA9082069A50E0085AE16 /* objc_utils.cpp */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + AD787AE820D4611100C4712A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AD58E0471E107411006ACC15 /* Project object */; + proxyType = 1; + remoteGlobalIDString = AD787ADC20D4611100C4712A; + remoteInfo = PanoQL; + }; + AD787AF920D4637E00C4712A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AD58E0471E107411006ACC15 /* Project object */; + proxyType = 1; + remoteGlobalIDString = AD787AF220D4637E00C4712A; + remoteInfo = PanoThumb; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ AD06989C20CC89370010825F /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; @@ -271,6 +296,18 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AD787AEE20D4611100C4712A /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + AD787AEA20D4611100C4712A /* PanoQL.appex in Embed App Extensions */, + AD787AFB20D4637E00C4712A /* PanoThumb.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -385,6 +422,16 @@ AD759B641F2793AE00211963 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; }; AD759B661F27964D00211963 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.0.sdk/System/Library/Frameworks/GLKit.framework; sourceTree = DEVELOPER_DIR; }; AD759B671F2796EA00211963 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; }; + AD787ADD20D4611100C4712A /* PanoQL.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PanoQL.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + AD787ADE20D4611100C4712A /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; }; + AD787AE120D4611100C4712A /* PreviewViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PreviewViewController.h; sourceTree = ""; }; + AD787AE220D4611100C4712A /* PreviewViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PreviewViewController.m; sourceTree = ""; }; + AD787AE520D4611100C4712A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + AD787AE720D4611100C4712A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AD787AF320D4637E00C4712A /* PanoThumb.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PanoThumb.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + AD787AF520D4637E00C4712A /* ThumbnailProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThumbnailProvider.h; sourceTree = ""; }; + AD787AF620D4637E00C4712A /* ThumbnailProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThumbnailProvider.m; sourceTree = ""; }; + AD787AF820D4637E00C4712A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AD8CF71F1E913F0500083FFD /* log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log.cpp; sourceTree = ""; }; AD8CF7201E913F0500083FFD /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; }; AD95AEC31E41EDEC002DD03A /* font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font.cpp; sourceTree = ""; }; @@ -464,6 +511,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AD787ADA20D4611100C4712A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AD787ADF20D4611100C4712A /* QuickLook.framework in Frameworks */, + AD787B0020D5AD1700C4712A /* ZipArchive.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AD787AF020D4637E00C4712A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AD787AFF20D5AD1500C4712A /* ZipArchive.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; ADD7D2681EBF9AE300D5A897 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -604,6 +668,8 @@ AD58E0511E107411006ACC15 /* engine */, ADD7D26C1EBF9AE300D5A897 /* PanoPainter */, AD0E5C9D1ECC6F2B00C35669 /* PanoPainter-OSX */, + AD787AE020D4611100C4712A /* PanoQL */, + AD787AF420D4637E00C4712A /* PanoThumb */, AD58E0501E107411006ACC15 /* Products */, AD759B631F2793AD00211963 /* Frameworks */, ); @@ -615,6 +681,8 @@ AD58E04F1E107411006ACC15 /* engine */, ADD7D26B1EBF9AE300D5A897 /* PanoPainter.app */, AD0E5C9C1ECC6F2B00C35669 /* PanoPainter.app */, + AD787ADD20D4611100C4712A /* PanoQL.appex */, + AD787AF320D4637E00C4712A /* PanoThumb.appex */, ); name = Products; sourceTree = ""; @@ -709,10 +777,32 @@ AD30D82E1F783E0100B6A112 /* libcurl.4.tbd */, AD759B661F27964D00211963 /* GLKit.framework */, AD759B641F2793AE00211963 /* OpenGLES.framework */, + AD787ADE20D4611100C4712A /* QuickLook.framework */, ); name = Frameworks; sourceTree = ""; }; + AD787AE020D4611100C4712A /* PanoQL */ = { + isa = PBXGroup; + children = ( + AD787AE120D4611100C4712A /* PreviewViewController.h */, + AD787AE220D4611100C4712A /* PreviewViewController.m */, + AD787AE420D4611100C4712A /* MainInterface.storyboard */, + AD787AE720D4611100C4712A /* Info.plist */, + ); + path = PanoQL; + sourceTree = ""; + }; + AD787AF420D4637E00C4712A /* PanoThumb */ = { + isa = PBXGroup; + children = ( + AD787AF520D4637E00C4712A /* ThumbnailProvider.h */, + AD787AF620D4637E00C4712A /* ThumbnailProvider.m */, + AD787AF820D4637E00C4712A /* Info.plist */, + ); + path = PanoThumb; + sourceTree = ""; + }; ADD7D26C1EBF9AE300D5A897 /* PanoPainter */ = { isa = PBXGroup; children = ( @@ -789,6 +879,40 @@ productReference = AD58E04F1E107411006ACC15 /* engine */; productType = "com.apple.product-type.tool"; }; + AD787ADC20D4611100C4712A /* PanoQL */ = { + isa = PBXNativeTarget; + buildConfigurationList = AD787AEB20D4611100C4712A /* Build configuration list for PBXNativeTarget "PanoQL" */; + buildPhases = ( + AD787AD920D4611100C4712A /* Sources */, + AD787ADA20D4611100C4712A /* Frameworks */, + AD787ADB20D4611100C4712A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PanoQL; + productName = PanoQL; + productReference = AD787ADD20D4611100C4712A /* PanoQL.appex */; + productType = "com.apple.product-type.app-extension"; + }; + AD787AF220D4637E00C4712A /* PanoThumb */ = { + isa = PBXNativeTarget; + buildConfigurationList = AD787AFC20D4637F00C4712A /* Build configuration list for PBXNativeTarget "PanoThumb" */; + buildPhases = ( + AD787AEF20D4637E00C4712A /* Sources */, + AD787AF020D4637E00C4712A /* Frameworks */, + AD787AF120D4637E00C4712A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PanoThumb; + productName = PanoThumb; + productReference = AD787AF320D4637E00C4712A /* PanoThumb.appex */; + productType = "com.apple.product-type.app-extension"; + }; ADD7D26A1EBF9AE300D5A897 /* PanoPainter */ = { isa = PBXNativeTarget; buildConfigurationList = ADD7D2851EBF9AE300D5A897 /* Build configuration list for PBXNativeTarget "PanoPainter" */; @@ -797,10 +921,13 @@ ADD7D2681EBF9AE300D5A897 /* Frameworks */, ADD7D2691EBF9AE300D5A897 /* Resources */, AD06989C20CC89370010825F /* CopyFiles */, + AD787AEE20D4611100C4712A /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + AD787AE920D4611100C4712A /* PBXTargetDependency */, + AD787AFA20D4637E00C4712A /* PBXTargetDependency */, ); name = PanoPainter; productName = PanoPainter; @@ -826,6 +953,16 @@ DevelopmentTeam = A6Y3VHN7V9; ProvisioningStyle = Automatic; }; + AD787ADC20D4611100C4712A = { + CreatedOnToolsVersion = 9.4.1; + DevelopmentTeam = ERD9AYQ49S; + ProvisioningStyle = Automatic; + }; + AD787AF220D4637E00C4712A = { + CreatedOnToolsVersion = 9.4.1; + DevelopmentTeam = ERD9AYQ49S; + ProvisioningStyle = Automatic; + }; ADD7D26A1EBF9AE300D5A897 = { CreatedOnToolsVersion = 7.2; DevelopmentTeam = ERD9AYQ49S; @@ -848,6 +985,8 @@ AD58E04E1E107411006ACC15 /* engine */, ADD7D26A1EBF9AE300D5A897 /* PanoPainter */, AD0E5C9B1ECC6F2B00C35669 /* PanoPainter-OSX */, + AD787ADC20D4611100C4712A /* PanoQL */, + AD787AF220D4637E00C4712A /* PanoThumb */, ); }; /* End PBXProject section */ @@ -863,6 +1002,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AD787ADB20D4611100C4712A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AD787AE620D4611100C4712A /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AD787AF120D4637E00C4712A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; ADD7D2691EBF9AE300D5A897 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1019,6 +1173,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AD787AD920D4611100C4712A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AD787AE320D4611100C4712A /* PreviewViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AD787AEF20D4637E00C4712A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AD787AF720D4637E00C4712A /* ThumbnailProvider.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; ADD7D2671EBF9AE300D5A897 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1097,6 +1267,19 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + AD787AE920D4611100C4712A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = AD787ADC20D4611100C4712A /* PanoQL */; + targetProxy = AD787AE820D4611100C4712A /* PBXContainerItemProxy */; + }; + AD787AFA20D4637E00C4712A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = AD787AF220D4637E00C4712A /* PanoThumb */; + targetProxy = AD787AF920D4637E00C4712A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ AD0E5CA61ECC6F2B00C35669 /* MainMenu.xib */ = { isa = PBXVariantGroup; @@ -1106,6 +1289,14 @@ name = MainMenu.xib; sourceTree = ""; }; + AD787AE420D4611100C4712A /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + AD787AE520D4611100C4712A /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; ADD7D27A1EBF9AE300D5A897 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -1302,6 +1493,176 @@ }; name = Release; }; + AD787AEC20D4611100C4712A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ERD9AYQ49S; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/libs/ZipArchive/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = PanoQL/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.omixlab.panopainter.ios.PanoQL; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + AD787AED20D4611100C4712A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ERD9AYQ49S; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/libs/ZipArchive/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = PanoQL/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.omixlab.panopainter.ios.PanoQL; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + AD787AFD20D4637F00C4712A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ERD9AYQ49S; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/libs/ZipArchive/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = PanoThumb/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.omixlab.panopainter.ios.PanoThumb; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + AD787AFE20D4637F00C4712A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ERD9AYQ49S; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/libs/ZipArchive/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = PanoThumb/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.omixlab.panopainter.ios.PanoThumb; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; ADD7D2831EBF9AE300D5A897 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1402,6 +1763,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + AD787AEB20D4611100C4712A /* Build configuration list for PBXNativeTarget "PanoQL" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AD787AEC20D4611100C4712A /* Debug */, + AD787AED20D4611100C4712A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AD787AFC20D4637F00C4712A /* Build configuration list for PBXNativeTarget "PanoThumb" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AD787AFD20D4637F00C4712A /* Debug */, + AD787AFE20D4637F00C4712A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; ADD7D2851EBF9AE300D5A897 /* Build configuration list for PBXNativeTarget "PanoPainter" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/engine.xcodeproj/xcuserdata/omimac.xcuserdatad/xcschemes/xcschememanagement.plist b/engine.xcodeproj/xcuserdata/omimac.xcuserdatad/xcschemes/xcschememanagement.plist index 91804c0..e9bbf07 100644 --- a/engine.xcodeproj/xcuserdata/omimac.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/engine.xcodeproj/xcuserdata/omimac.xcuserdatad/xcschemes/xcschememanagement.plist @@ -14,6 +14,16 @@ orderHint 1 + PanoQL.xcscheme + + orderHint + 3 + + PanoThumb.xcscheme + + orderHint + 4 + engine.xcscheme orderHint diff --git a/engine/app.cpp b/engine/app.cpp index dc7f78f..0a32898 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -33,7 +33,9 @@ void App::create() bool App::request_close() { static bool dialog_already_opened = false; - if (ui::Canvas::I->m_unsaved && !dialog_already_opened) + if (!ui::Canvas::I->m_unsaved) + return true; + if (!dialog_already_opened) { async_start(); auto* m = layout[main_id]->add_child(); @@ -43,9 +45,15 @@ bool App::request_close() m->btn_ok->on_click = [](Node*) { }; m->btn_cancel->m_text->set_text("No"); - m->btn_cancel->on_click = [](Node*) { + m->btn_cancel->on_click = [this](Node*) { + #ifdef _WIN32 destroy_window(); PostQuitMessage(0); + #endif + #ifdef __OSX__ + [osx_view close]; + #endif + ui::Canvas::I->m_unsaved = false; }; async_redraw(); async_end(); diff --git a/engine/canvas_modes.cpp b/engine/canvas_modes.cpp index 3f0a076..58c9c32 100644 --- a/engine/canvas_modes.cpp +++ b/engine/canvas_modes.cpp @@ -160,9 +160,11 @@ void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { #ifndef __IOS__ - if (!m_dragging && !m_picking || m_resizing) + if (1) { auto pos = m_resizing ? m_size_pos_start : m_cur_pos; + if (App::I.keys[(int)kKey::KeyAlt]) + pos.x = pos.x - canvas->m_current_brush.m_tip_size * 500; ui::ShaderManager::use(ui::kShader::StrokePreview); ui::ShaderManager::u_int(ui::kShaderUniform::Tex, 0); ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, canvas->m_current_brush.m_tip_flow);