Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Get basic Core Text drawing working

  • Loading branch information...
commit 8c493c8d17d315e4c1f94b768d0ed646c75c64ab 1 parent 6d97aa8
@jjgod authored
View
2  AppController.h
@@ -8,7 +8,7 @@
#import <Cocoa/Cocoa.h>
-@interface AppController : NSObject {
+@interface AppController : NSResponder {
}
View
15 AppController.m
@@ -104,15 +104,24 @@ - (BOOL) applicationShouldOpenUntitledFile: (NSApplication *) sender
return NO;
}
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
- (void) changeFont: (id) sender
{
NSFont *oldFont = [self font];
NSFont *newFont = [sender convertFont: oldFont];
-
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
NSLog(@"changeFont = %@", newFont);
- [[NSUserDefaults standardUserDefaults] setValue: [newFont fontName] forKey: @"fontName"];
- [[NSUserDefaults standardUserDefaults] setValue: [NSNumber numberWithDouble: [newFont pointSize]] forKey: @"fontSize"];
+ if ([oldFont fontName] != [newFont fontName])
+ [defaults setValue: [newFont fontName] forKey: @"fontName"];
+
+ if ([oldFont pointSize] != [newFont pointSize])
+ [defaults setValue: [NSNumber numberWithDouble: [newFont pointSize]] forKey: @"fontSize"];
}
@end
View
16 English.lproj/MainMenu.xib
@@ -2,9 +2,9 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
<data>
<int key="IBDocument.SystemTarget">1050</int>
- <string key="IBDocument.SystemVersion">9G55</string>
+ <string key="IBDocument.SystemVersion">9J61</string>
<string key="IBDocument.InterfaceBuilderVersion">677</string>
- <string key="IBDocument.AppKitVersion">949.43</string>
+ <string key="IBDocument.AppKitVersion">949.46</string>
<string key="IBDocument.HIToolboxVersion">353.00</string>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -826,7 +826,7 @@
<object class="NSTextFieldCell" key="NSCell" id="986725230">
<int key="NSCellFlags">68288064</int>
<int key="NSCellFlags2">71304192</int>
- <string key="NSContents">Line Spacing:</string>
+ <string key="NSContents">Line Spread:</string>
<reference key="NSSupport" ref="901208874"/>
<reference key="NSControlView" ref="995010602"/>
<reference key="NSBackgroundColor" ref="248392606"/>
@@ -873,8 +873,8 @@
<int key="NSCellFlags">917024</int>
<int key="NSCellFlags2">0</int>
<reference key="NSControlView" ref="228139828"/>
- <double key="NSMaxValue">1.000000e+02</double>
- <double key="NSIncrement">1.000000e+00</double>
+ <double key="NSMaxValue">1.000000e+01</double>
+ <double key="NSIncrement">1.000000e-01</double>
<bool key="NSAutorepeat">YES</bool>
</object>
</object>
@@ -2237,8 +2237,8 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<reference ref="9"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>{{321, 407}, {200, 170}}</string>
- <string>{{321, 407}, {200, 170}}</string>
+ <string>{{337, 443}, {200, 170}}</string>
+ <string>{{337, 443}, {200, 170}}</string>
<boolean value="NO"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2320,7 +2320,7 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">AppController</string>
- <string key="superclassName">NSObject</string>
+ <string key="superclassName">NSResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">AppController.h</string>
View
14 English.lproj/TextDocument.xib
@@ -34,7 +34,7 @@
<object class="NSWindowTemplate" id="275939982">
<int key="NSWindowStyleMask">15</int>
<int key="NSWindowBacking">2</int>
- <string key="NSWindowRect">{{103, -42}, {939, 787}}</string>
+ <string key="NSWindowRect">{{103, -31}, {939, 776}}</string>
<int key="NSWTFlags">1886912512</int>
<string key="NSWindowTitle">Window</string>
<string key="NSWindowClass">NSWindow</string>
@@ -59,12 +59,12 @@
<object class="NSCustomView" id="793227834">
<reference key="NSNextResponder" ref="479699895"/>
<int key="NSvFlags">274</int>
- <string key="NSFrameSize">{939, 766}</string>
+ <string key="NSFrameSize">{939, 755}</string>
<reference key="NSSuperview" ref="479699895"/>
<string key="NSClassName">JJTextView</string>
</object>
</object>
- <string key="NSFrame">{{1, 1}, {939, 766}}</string>
+ <string key="NSFrame">{{1, 1}, {939, 755}}</string>
<reference key="NSSuperview" ref="707828383"/>
<reference key="NSNextKeyView" ref="793227834"/>
<reference key="NSDocView" ref="793227834"/>
@@ -100,7 +100,7 @@
<double key="NSPercent">5.060241e-01</double>
</object>
</object>
- <string key="NSFrame">{{-1, 20}, {941, 768}}</string>
+ <string key="NSFrame">{{-1, 20}, {941, 757}}</string>
<reference key="NSSuperview" ref="568628114"/>
<reference key="NSNextKeyView" ref="479699895"/>
<int key="NSsFlags">562</int>
@@ -109,7 +109,7 @@
<reference key="NSContentView" ref="479699895"/>
</object>
</object>
- <string key="NSFrameSize">{939, 787}</string>
+ <string key="NSFrameSize">{939, 776}</string>
<reference key="NSSuperview"/>
</object>
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
@@ -263,8 +263,8 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>{{389, 266}, {939, 787}}</string>
- <string>{{389, 266}, {939, 787}}</string>
+ <string>{{389, 80}, {939, 776}}</string>
+ <string>{{389, 80}, {939, 776}}</string>
<integer value="1" id="9"/>
<string>{{201, 387}, {507, 413}}</string>
<reference ref="9"/>
View
2  Info.plist
@@ -42,7 +42,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>1.0</string>
+ <string>2.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
View
14 JJTextView.h
@@ -9,20 +9,16 @@
#import <Cocoa/Cocoa.h>
@interface JJTextView : NSView {
- NSSize textContainerInset;
- CGFloat lineGap;
+ NSSize textInset;
NSColor *backgroundColor;
- NSFont *font;
NSString *string;
- CTFrameRef frame;
+ NSMutableArray *textFrames;
}
-@property (assign) NSSize textContainerInset;
-@property (assign) CGFloat lineGap;
+@property (assign) NSSize textInset;
@property (retain) NSColor *backgroundColor;
-@property (retain) NSFont *font;
-@property (copy) NSString *string;
-- (void) relayout;
+- (void) setString: (NSString *) string;
+- (void) invalidateLayout;
@end
View
207 JJTextView.m
@@ -8,27 +8,51 @@
#import "JJTextView.h"
+void CTFrameDrawLines(CTFrameRef frame, CGContextRef context)
+{
+ CFArrayRef lines = CTFrameGetLines(frame);
+ CFIndex i, total = CFArrayGetCount(lines);
+ CGPoint origins[255];
+ CGRect rect = CGPathGetBoundingBox(CTFrameGetPath(frame));
+ CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
+
+ // NSLog(@"bounding rect: %@", NSStringFromRect(NSRectFromCGRect(rect)));
+ for (i = 0; i < total; i++)
+ {
+ CTLineRef line = CFArrayGetValueAtIndex(lines, i);
+ // NSLog(@"origins[%d].y = %g", i, rect.size.height - origins[i].y);
+ CGContextSetTextPosition(context,
+ rect.origin.x + origins[i].x,
+ rect.origin.y + rect.size.height - origins[i].y);
+ CTLineDraw(line, context);
+ }
+}
+
@implementation JJTextView
-@synthesize textContainerInset;
-@synthesize lineGap;
+@synthesize textInset;
@synthesize backgroundColor;
-@synthesize font;
-@synthesize string;
+
+- (id) initWithFrame: (NSRect) frameRect
+{
+ if ((self = [super initWithFrame: frameRect]))
+ {
+ textInset = NSMakeSize(20, 20);
+ textFrames = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
- (void) awakeFromNib
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- NSArray *keyPaths = [NSArray arrayWithObjects: @"backgroundColor", @"lineHeight", nil];
+ NSArray *keyPaths = [NSArray arrayWithObjects: @"backgroundColor", @"lineHeight", @"fontName", @"fontSize", nil];
for (NSString *keyPath in keyPaths)
[defaults addObserver: self
forKeyPath: keyPath
options: 0
context: nil];
-
- self.textContainerInset = NSMakeSize(20, 20);
- self.lineGap = [defaults doubleForKey: @"lineHeight"];
}
- (void) dealloc
@@ -37,45 +61,131 @@ - (void) dealloc
forKeyPath: @"backgroundColor"];
[[NSUserDefaults standardUserDefaults] removeObserver: self
forKeyPath: @"lineHeight"];
+ [string release];
+ string = nil;
+ [textFrames release];
+ textFrames = nil;
+
[super dealloc];
}
-- (void) relayout
+- (void) setString: (NSString *) str
{
- NSLog(@"self string: %@", [self string]);
- if (string)
+ string = [str retain];
+ [self invalidateLayout];
+}
+
+- (void) invalidateLayout
+{
+ if (! string)
+ return;
+
+ NSRect rect = [[self enclosingScrollView] documentVisibleRect];
+ NSLog(@"rect to draw: %@", NSStringFromRect(rect));
+ NSRect newFrame = rect;
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ CGFloat lineHeight = [defaults doubleForKey: @"lineHeight"];
+
+ CTParagraphStyleSetting settings[] = {
+ { kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(CGFloat), &lineHeight },
+ };
+ CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
+ NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString: string];
+ NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSFont fontWithName: [defaults stringForKey: @"fontName"]
+ size: [defaults doubleForKey: @"fontSize"]],
+ (NSString *) kCTFontAttributeName,
+ paragraphStyle, (NSString *) kCTParagraphStyleAttributeName,
+ nil];
+ CFRelease(paragraphStyle);
+
+ [attrString setAttributes: attributes
+ range: NSMakeRange(0, string.length)];
+
+ // Create the framesetter with the attributed string.
+ CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef) attrString);
+ [attrString release];
+
+ CFRange fullRange = CFRangeMake(0, string.length);
+ CGRect frameRect = CGRectMake(textInset.width, textInset.height,
+ rect.size.width - 2 * textInset.width,
+ rect.size.height - textInset.height);
+
+ [textFrames removeAllObjects];
+ CFRange range, frameRange;
+ for (range = frameRange = CFRangeMake(0, 0);
+ range.location < fullRange.length;
+ range.location += frameRange.length)
{
- // Initialize a rectangular path.
CGMutablePathRef path = CGPathCreateMutable();
- CGPathAddRect(path, NULL, NSRectToCGRect([self frame]));
+ CGPathAddRect(path, NULL, frameRect);
- NSAttributedString *attrString = [[NSAttributedString alloc] initWithString: string];
+ CTFrameRef frame = CTFramesetterCreateFrame(framesetter, range, path, NULL);
+ frameRange = CTFrameGetVisibleStringRange(frame);
+#if 0
+ NSLog(@"frameRange: %ld, %ld, %@",
+ frameRange.location, frameRange.length,
+ NSStringFromRect(NSRectFromCGRect(frameRect)));
+#endif
+ // range.location += frameRange.length;
+ frameRect.origin.y += frameRect.size.height;
+ frameRect.size.height = rect.size.height;
- // Create the framesetter with the attributed string.
- CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef) attrString);
- [attrString release];
+ [textFrames addObject: (id) frame];
- if (frame)
- CFRelease(frame);
+ CFRelease(path);
+ CFRelease(frame);
+ }
- // Create the frame and draw it into the graphics context
- frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
+ CFRelease(framesetter);
- CFRelease(framesetter);
- }
+ newFrame.size.height = frameRect.origin.y;
+ [self setFrame: newFrame];
+ [self setNeedsDisplay: YES];
}
-- (void) drawRect: (NSRect) rect
+- (BOOL) isFlipped
{
- NSLog(@"rect: %@", NSStringFromRect(rect));
+ return YES;
+}
- [self relayout];
+- (void) drawRect: (NSRect) rect
+{
+ // NSLog(@"rect: %@", NSStringFromRect(rect));
// Initialize a graphics context and set the text matrix to a known value.
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
- CGContextSetTextMatrix(context, CGAffineTransformIdentity);
- CTFrameDraw(frame, context);
+ CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1, -1));
+
+ CTFrameRef frame = (CTFrameRef) [textFrames objectAtIndex: 1];
+ if (! frame)
+ frame = (CTFrameRef) [textFrames objectAtIndex: 0];
+ if (! frame)
+ return;
+
+ CGRect bounds = CGPathGetBoundingBox(CTFrameGetPath(frame));
+ NSUInteger i, start = (rect.origin.y - textInset.height) / bounds.size.height;
+
+ for (i = start; i < start + 2 && i < [textFrames count]; i++)
+ {
+ frame = (CTFrameRef) [textFrames objectAtIndex: i];
+
+#if 0
+ NSLog(@"drawing frame: %lu, rect: %@", i, NSStringFromRect(NSRectFromCGRect(bounds)));
+ CGRect bounds = CGPathGetBoundingBox(CTFrameGetPath(frame));
+
+ CGContextSetRGBFillColor(context, 0.1, 0.7, 0.7, 1.0);
+ NSRectFill(NSRectFromCGRect(bounds));
+
+ [[NSColor redColor] set];
+ NSRectFill(NSMakeRect(200, bounds.origin.y, 50, 1.5));
+
+ [[NSColor blackColor] set];
+ NSRectFill(NSMakeRect(0, bounds.origin.y + bounds.size.height, 50, 1.5));
+#endif
+ CTFrameDrawLines((CTFrameRef) frame, context);
+ }
}
- (void) observeValueForKeyPath: (NSString *) keyPath
@@ -88,11 +198,10 @@ - (void) observeValueForKeyPath: (NSString *) keyPath
if ([keyPath isEqual: @"backgroundColor"])
self.backgroundColor = [[NSApp delegate] backgroundColor];
- else if ([keyPath isEqual: @"lineHeight"])
- {
- self.lineGap = [[NSUserDefaults standardUserDefaults] doubleForKey: @"lineHeight"];
- [self relayout];
- }
+ else if ([keyPath isEqual: @"lineHeight"] ||
+ [keyPath isEqual: @"fontName"] ||
+ [keyPath isEqual: @"fontSize"])
+ [self invalidateLayout];
}
- (void) scrollTo: (float) y
@@ -115,6 +224,7 @@ - (void) scrollBy: (float) value
- (BOOL) processKey: (int) ch
{
float y;
+ CGFloat pageHeight = [(NSScrollView *) [self superview] documentVisibleRect].size.height;
switch (ch)
{
@@ -128,27 +238,27 @@ - (BOOL) processKey: (int) ch
case ' ':
case NSPageDownFunctionKey:
- [self scrollPageDown:self];
+ [self scrollBy: pageHeight];
break;
case NSPageUpFunctionKey:
- [self scrollPageUp:self];
+ [self scrollBy: -pageHeight];
break;
case NSEndFunctionKey:
y = NSMaxY([[[self enclosingScrollView] documentView] frame]) -
- NSHeight([[[self enclosingScrollView] contentView] bounds]);
+ NSHeight([[[self enclosingScrollView] contentView] bounds]);
[self scrollTo: y];
break;
-
+
case NSHomeFunctionKey:
- [self scrollTo:0.0];
+ [self scrollTo: 0];
break;
-
+
default:
return NO;
}
-
+
return YES;
}
@@ -167,21 +277,4 @@ - (void) keyDown: (NSEvent *) event
}
}
-- (BOOL) acceptsFirstResponder
-{
- return YES;
-}
-
-- (void) changeFont: (id) sender
-{
- NSFont *oldFont = [self font];
- NSFont *newFont = [sender convertFont: oldFont];
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-
- NSLog(@"changeFont = %@", newFont);
-
- [defaults setValue: [newFont fontName] forKey: @"fontName"];
- [defaults setValue: [NSNumber numberWithDouble: [newFont pointSize]] forKey: @"fontSize"];
-}
-
@end
View
13 README.markdown
@@ -0,0 +1,13 @@
+Textus
+======
+
+A new ebook reader on Mac OS X. Powered by Core Text, super fast.
+
+TODO
+----
+
+* Fix last frame height too long problem
+* Use line-scale redraw to further optimize performance
+* Vertical layout
+* On-demand layout (according to pages)
+
View
4 TextDocument.m
@@ -40,11 +40,7 @@ - (void) windowControllerDidLoadNib: (NSWindowController *) aController
[textView setBackgroundColor: [(AppController *) [NSApp delegate] backgroundColor]];
if (fileContents)
- {
[textView setString: fileContents];
- [textView scrollPoint: NSMakePoint(0.0, 0)];
- [textView setNeedsDisplay: YES];
- }
}
- (NSData *) dataOfType: (NSString *) typeName

0 comments on commit 8c493c8

Please sign in to comment.
Something went wrong with that request. Please try again.