From dd0deb9758dc53fd50408b268f858bf4635d2f39 Mon Sep 17 00:00:00 2001 From: JugglerShu Date: Mon, 21 Sep 2015 01:24:38 +0900 Subject: [PATCH] Fix that status line doesn't show correctly in Xcode 6.4 and 7. This fix completely rewrite the status line implementation. It uses autolayout and binding to show a status line at right position. This may fix the issue #830 since now it uses color from the theme. --- XVim/IDEEditor+XVim.h | 4 +- XVim/IDEEditor+XVim.m | 53 +------------ XVim/IDESourceCodeEditor+XVim.h | 1 + XVim/IDESourceCodeEditor+XVim.m | 79 +++++++++++++++++++ XVim/Logger.m | 8 +- XVim/XVim.m | 9 --- XVim/XVimStatusLine.h | 13 +-- XVim/XVimStatusLine.m | 136 +++++++++----------------------- 8 files changed, 130 insertions(+), 173 deletions(-) diff --git a/XVim/IDEEditor+XVim.h b/XVim/IDEEditor+XVim.h index 876abfa3..0395f822 100644 --- a/XVim/IDEEditor+XVim.h +++ b/XVim/IDEEditor+XVim.h @@ -12,6 +12,4 @@ @interface IDEEditor(XVim) + (void)xvim_initialize; -- (void)xvim_didSetupEditor; -- (void)xvim_primitiveInvalidate; -@end +@end \ No newline at end of file diff --git a/XVim/IDEEditor+XVim.m b/XVim/IDEEditor+XVim.m index 2b6da911..2f221bfa 100644 --- a/XVim/IDEEditor+XVim.m +++ b/XVim/IDEEditor+XVim.m @@ -11,66 +11,17 @@ #import "IDESourceEditor.h" #import "Logger.h" #import "XVim.h" +#import "XVimOptions.h" +#import "NSObject+ExtraData.h" #import "XVimStatusLine.h" #import #import "NSObject+XVimAdditions.h" -#define DID_REGISTER_OBSERVER_KEY "net.JugglerShu.IDEEditorHook._didRegisterObserver" @implementation IDEEditor(XVim) + (void)xvim_initialize{ - [self xvim_swizzleInstanceMethod:@selector(didSetupEditor) with:@selector(xvim_didSetupEditor)]; - [self xvim_swizzleInstanceMethod:@selector(primitiveInvalidate) with:@selector(xvim_primitiveInvalidate)]; } -- (void)xvim_didSetupEditor{ - - [self xvim_didSetupEditor]; - - // If you do not like status line comment out folloing. - // ---- FROM HERE ---- - NSView* container = nil; - if( [NSStringFromClass([self class]) isEqualToString:@"IDESourceCodeComparisonEditor"] ){ - container = [(IDESourceCodeComparisonEditor*)self layoutView]; - } - else if( [NSStringFromClass([self class]) isEqualToString:@"IDESourceCodeEditor"] ){ - container = [(IDESourceCodeEditor*)self containerView]; - }else{ - return; - } - - if (container != nil) { - XVimStatusLine *status = [XVimStatusLine associateOf:container]; - if (status == nil) { - // Insert status line - [container setPostsFrameChangedNotifications:YES]; - status = [[XVimStatusLine alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; - [container addSubview:status]; - [status associateWith:container]; - status.editor = self; - - // Layout - [[NSNotificationCenter defaultCenter] addObserver:status selector:@selector(didContainerFrameChanged:) name:NSViewFrameDidChangeNotification object:container]; - [status layoutStatus:container]; - [container performSelector:@selector(invalidateLayout)]; - - // For % register and to notify contents of editor is changed - [self addObserver:[XVim instance] forKeyPath:@"document" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:(__bridge void*)self]; - objc_setAssociatedObject(self, DID_REGISTER_OBSERVER_KEY, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN); - } - } - //---- TO HERE ---- -} - -- (void)xvim_primitiveInvalidate { - IDEEditor *editor = (IDEEditor *)self; - NSNumber *didRegisterObserver = objc_getAssociatedObject(editor, DID_REGISTER_OBSERVER_KEY); - if ([didRegisterObserver boolValue]) { - [editor removeObserver:[XVim instance] forKeyPath:@"document"]; - } - - [self xvim_primitiveInvalidate]; -} @end diff --git a/XVim/IDESourceCodeEditor+XVim.h b/XVim/IDESourceCodeEditor+XVim.h index b7d41c84..c331036e 100644 --- a/XVim/IDESourceCodeEditor+XVim.h +++ b/XVim/IDESourceCodeEditor+XVim.h @@ -12,4 +12,5 @@ @interface IDESourceCodeEditor(XVim) + (void)xvim_initialize; - (NSArray*) xvim_textView:(NSTextView *)textView willChangeSelectionFromCharacterRanges:(NSArray *)oldSelectedCharRanges toCharacterRanges:(NSArray *)newSelectedCharRanges; +- (id)xvim_initWithNibName:(NSString*)arg1 bundle:(NSBundle*)arg2 document:(IDEEditorDocument*)arg3; @end \ No newline at end of file diff --git a/XVim/IDESourceCodeEditor+XVim.m b/XVim/IDESourceCodeEditor+XVim.m index d6831455..80a02e2d 100644 --- a/XVim/IDESourceCodeEditor+XVim.m +++ b/XVim/IDESourceCodeEditor+XVim.m @@ -8,19 +8,98 @@ #import "IDESourceCodeEditor+XVim.h" #import "IDEKit.h" +#import "DVTFoundation.h" #import "XVimWindow.h" #import "Logger.h" #import "XVimStatusLine.h" #import "XVim.h" #import "NSObject+XVimAdditions.h" +#import "NSobject+ExtraData.h" @implementation IDESourceCodeEditor(XVim) + (void)xvim_initialize{ [self xvim_swizzleInstanceMethod:@selector(textView:willChangeSelectionFromCharacterRanges:toCharacterRanges:) with:@selector(xvim_textView:willChangeSelectionFromCharacterRanges:toCharacterRanges:)]; + [self xvim_swizzleInstanceMethod:@selector(initWithNibName:bundle:document:) with:@selector(xvim_initWithNibName:bundle:document:)]; } - (NSArray*) xvim_textView:(NSTextView *)textView willChangeSelectionFromCharacterRanges:(NSArray *)oldSelectedCharRanges toCharacterRanges:(NSArray *)newSelectedCharRanges { return newSelectedCharRanges; } + +- (id)xvim_initWithNibName:(NSString*)name bundle:(NSBundle*)bundle document:(IDEEditorDocument*)doc{ + id obj = [self xvim_initWithNibName:name bundle:bundle document:doc]; + NSView* container = [[obj view] contentView]; + + // Insert status line + if( nil != container ){ + // TODO: Observe DVTFontAndColorSourceTextSettingsChangedNotification to change color of status bar + DVTSourceTextScrollView* scrollView = [self mainScrollView]; + [scrollView setTranslatesAutoresizingMaskIntoConstraints:NO]; // To use autolayout we need set this NO + + // Add status view + XVimStatusLine* status = [[XVimStatusLine alloc] initWithString:doc.filePath.pathString]; + [status setTranslatesAutoresizingMaskIntoConstraints:NO]; + [container addSubview:status]; + + // Bind its visibility to 'laststatus' + XVimLaststatusTransformer* transformer = [[XVimLaststatusTransformer alloc] init]; + [status bind:@"hidden" toObject:[[XVim instance] options] withKeyPath:@"laststatus" options:@{NSValueTransformerBindingOption:transformer}]; + + + // View autolayout constraints (for the source view and status bar) + + // Same width with the parent + [container addConstraint:[NSLayoutConstraint constraintWithItem:scrollView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:container + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:0.0]]; + + // ScrollView's left position is 0 + [container addConstraint:[NSLayoutConstraint constraintWithItem:scrollView + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:container + attribute:NSLayoutAttributeLeft + multiplier:1.0 + constant:0.0]]; + // Position scrollView above the status bar + [container addConstraint:[NSLayoutConstraint constraintWithItem:scrollView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:status + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0]]; + // ScrollView fills to top of the container view + [container addConstraint:[NSLayoutConstraint constraintWithItem:scrollView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:container + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0]]; + // Place Status line at bottom edge + [container addConstraint:[NSLayoutConstraint constraintWithItem:status + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:container + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0]]; + // Status line width fills the container + [container addConstraint:[NSLayoutConstraint constraintWithItem:status + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:container + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:0.0]]; + } + + return obj; +} @end \ No newline at end of file diff --git a/XVim/Logger.m b/XVim/Logger.m index 3881f7e3..f250be44 100644 --- a/XVim/Logger.m +++ b/XVim/Logger.m @@ -9,6 +9,7 @@ #import "Logger.h" #import #import +#import "DVTKit.h" #define LOGGER_DEFAULT_NAME @"LoggerDefaultName" @@ -235,14 +236,17 @@ + (void) traceViewInfo:(NSView*)obj subView:(BOOL)sub{ [Logger traceViewInfoImpl:obj subView:sub prefix:@""]; } + + (void)traceView:(NSView*)view depth:(NSUInteger)depth{ NSMutableString* str = [[NSMutableString alloc] init]; for( NSUInteger i = 0 ; i < depth; i++ ){ [str appendString:@" "]; } [str appendString:@"%p:%@ (Tag:%d)"]; - NSLog(str, view, NSStringFromClass([view class]), - [view tag]); + if( [view isKindOfClass:NSClassFromString(@"DVTControllerContentView")]){ + [str appendFormat:@" <--- %@", [(DVTControllerContentView*)view viewController].description]; + } + NSLog(str, view, NSStringFromClass([view class]), [view tag]); for(NSView* v in [view subviews] ){ [self traceView:v depth:depth+1]; } diff --git a/XVim/XVim.m b/XVim/XVim.m index 4626c999..5a5dee21 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -229,15 +229,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N }else{ [[Logger defaultLogger] setLogFile:nil]; } - } else if( [keyPath isEqualToString:@"document"] ){ - NSString *documentPath = [[[object document] fileURL] path]; - self.document = documentPath; - IDEEditor* editor = (__bridge IDEEditor*)context; - - if (documentPath != nil) { - NSDictionary *userInfo = @{XVimDocumentPathKey:documentPath, XVimStatusLineIDEEditorKey:editor}; - [[NSNotificationCenter defaultCenter] postNotificationName:XVimDocumentChangedNotification object:nil userInfo:userInfo]; - } } } diff --git a/XVim/XVimStatusLine.h b/XVim/XVimStatusLine.h index 16b827aa..6e340140 100644 --- a/XVim/XVimStatusLine.h +++ b/XVim/XVimStatusLine.h @@ -8,14 +8,9 @@ #import -extern NSString* const XVimStatusLineIDEEditorKey; - -@class IDEEditor; - -@interface XVimStatusLine : NSView -@property (weak,nonatomic) IDEEditor* editor; -- (void)layoutStatus:(NSView*)container; +@interface XVimLaststatusTransformer : NSValueTransformer +@end -+ (XVimStatusLine*)associateOf:(id)object; -- (void)associateWith:(id)object; +@interface XVimStatusLine : NSTextField +- (id)initWithString:(NSString*)str; @end diff --git a/XVim/XVimStatusLine.m b/XVim/XVimStatusLine.m index 1188c335..8eabe082 100644 --- a/XVim/XVimStatusLine.m +++ b/XVim/XVimStatusLine.m @@ -15,122 +15,60 @@ #import "XVim.h" #import "XVimOptions.h" -#define STATUS_LINE_HEIGHT 18 -#define MAX_STATUS_LINE_FONT_SIZE 14 - -NSString* const XVimStatusLineIDEEditorKey = @"IDEEditor"; - -@interface XVimStatusLine () - -- (void)_documentChangedNotification:(NSNotification *)notification; - -@end - -@implementation XVimStatusLine{ - DVTChooserView* _background; - NSInsetTextView* _status; +@implementation XVimLaststatusTransformer ++ (Class)transformedValueClass +{ + return [NSNumber class]; } -- (id)initWithFrame:(NSRect)frame ++ (BOOL)allowsReverseTransformation { - self = [super initWithFrame:frame]; - if (self) { - _background = [NSClassFromString(@"DVTChooserView").alloc init]; - _background.gradientStyle = 2; // Style number 2 looks like IDEGlassBarView - [_background setBorderSides:12]; // See DVTBorderedView.h for the meaning of the number - _status = [[NSInsetTextView alloc] initWithFrame:NSMakeRect(0, 0, 0, STATUS_LINE_HEIGHT)]; - _status.backgroundColor = [NSColor clearColor]; - [_status setEditable:NO]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_documentChangedNotification:) name:XVimDocumentChangedNotification object:nil]; - - [self addSubview:_background]; - [self addSubview:_status]; - } - - return self; + return NO; } -- (void)dealloc +- (id)transformedValue:(id)value { - [[NSNotificationCenter defaultCenter] removeObserver:self]; + if (value == nil) return nil; + + NSInteger laststatus = [value integerValue]; + // TODO: Handle "1" case correctly ("only if there are at least two window" case) + if( laststatus == 2 ){ + return [NSNumber numberWithBool:NO]; // HIDDEN = NO + } + return [NSNumber numberWithBool:YES]; // HIDDEN = YES } +@end -- (void)layoutStatus:(NSView*)container -{ - DVTFontAndColorTheme* theme = [NSClassFromString(@"DVTFontAndColorTheme") performSelector:@selector(currentTheme)]; - NSFont *sourceFont = [theme sourcePlainTextFont]; - - if (sourceFont.pointSize > MAX_STATUS_LINE_FONT_SIZE) { - sourceFont = [NSFont fontWithName:[sourceFont fontName] size:MAX_STATUS_LINE_FONT_SIZE]; - } - - // Calculate inset - CGFloat horizontalInset = 0; - CGFloat verticalInset = MAX((STATUS_LINE_HEIGHT - [sourceFont pointSize]) / 2, 0); - CGSize inset = CGSizeMake(horizontalInset, verticalInset); - - XVimOptions* options = [[XVim instance] options]; - CGFloat height; - if( options.laststatus == 2 ){ - height = STATUS_LINE_HEIGHT; - } else { - height = 0; - } - NSRect parentRect = [container frame]; - [self setFrame:NSMakeRect(0, 0, parentRect.size.width, height)]; - [_background setFrame:NSMakeRect(0, 0, parentRect.size.width, STATUS_LINE_HEIGHT)]; - [_status setFrame:NSMakeRect(0, 0, parentRect.size.width, STATUS_LINE_HEIGHT)]; - [_status setFont:sourceFont]; - [_status setInset:inset]; - [_status setBackgroundColor:[theme sourceTextBackgroundColor]]; - [_status setTextColor:[theme sourcePlainTextColor]]; +@implementation XVimStatusLine - // This is heuristic way... - if( [NSStringFromClass([container class]) isEqualToString:@"IDEComparisonEditorAutoLayoutView"] ){ - // Nothing ( Maybe AutoLayout view does the job "automatically") +- (NSSize)intrinsicContentSize{ + if( self.hidden ){ + return NSMakeSize(NSViewNoInstrinsicMetric, 0.0); }else{ - if( [container subviews].count > 0 ){ - [[[container subviews] objectAtIndex:0] setFrame:NSMakeRect(0, height, parentRect.size.width, parentRect.size.height-height)]; - } + return [super intrinsicContentSize]; } } - -- (void)didContainerFrameChanged:(NSNotification*)notification{ - // TODO: Find the way to get scrollView from IDESourceCodeEditor class. - // Now it is assumed that container view has the scrollView at index 0 of subviews. - NSView* container = [notification object]; - [self layoutStatus:container]; - -} -- (void)_documentChangedNotification:(NSNotification *)notification -{ - NSDictionary* dic = [notification userInfo]; - NSString *documentPath = dic[XVimDocumentPathKey]; - IDEEditor* editor = dic[XVimStatusLineIDEEditorKey]; - if (documentPath != nil && editor != nil && editor == self.editor ) { - [_status setString:documentPath]; - } +- (void)setHidden:(BOOL)hidden{ + [super setHidden:hidden]; + [self invalidateIntrinsicContentSize]; } -- (void)drawRect:(NSRect)dirtyRect -{ - // Drawing code here. +- (id)initWithString:(NSString *)str{ + self = [super init]; + if (self) { + DVTFontAndColorTheme* theme = [NSClassFromString(@"DVTFontAndColorTheme") performSelector:@selector(currentTheme)]; + NSFont *sourceFont = [theme sourcePlainTextFont]; + [self setBackgroundColor:[theme sourceTextSidebarBackgroundColor]]; + [self setFont:sourceFont]; + [self setEditable:NO]; + [self setSelectable:YES]; + [self setBordered:NO]; + [self setStringValue:str]; + } -} - -static char s_associate_key = 0; - -+ (XVimStatusLine*)associateOf:(id)object -{ - return (XVimStatusLine*)objc_getAssociatedObject(object, &s_associate_key); -} - -- (void)associateWith:(id)object -{ - objc_setAssociatedObject(object, &s_associate_key, self, OBJC_ASSOCIATION_RETAIN); + return self; } @end