Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Get basic Core Text drawing working

  • Loading branch information...
commit 8c493c8d17d315e4c1f94b768d0ed646c75c64ab 1 parent 6d97aa8
Jiang Jiang authored
2  AppController.h
@@ -8,7 +8,7 @@
8 8
9 9 #import <Cocoa/Cocoa.h>
10 10
11   -@interface AppController : NSObject {
  11 +@interface AppController : NSResponder {
12 12
13 13 }
14 14
15 AppController.m
@@ -104,15 +104,24 @@ - (BOOL) applicationShouldOpenUntitledFile: (NSApplication *) sender
104 104 return NO;
105 105 }
106 106
  107 +- (BOOL) acceptsFirstResponder
  108 +{
  109 + return YES;
  110 +}
  111 +
107 112 - (void) changeFont: (id) sender
108 113 {
109 114 NSFont *oldFont = [self font];
110 115 NSFont *newFont = [sender convertFont: oldFont];
111   -
  116 + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  117 +
112 118 NSLog(@"changeFont = %@", newFont);
113 119
114   - [[NSUserDefaults standardUserDefaults] setValue: [newFont fontName] forKey: @"fontName"];
115   - [[NSUserDefaults standardUserDefaults] setValue: [NSNumber numberWithDouble: [newFont pointSize]] forKey: @"fontSize"];
  120 + if ([oldFont fontName] != [newFont fontName])
  121 + [defaults setValue: [newFont fontName] forKey: @"fontName"];
  122 +
  123 + if ([oldFont pointSize] != [newFont pointSize])
  124 + [defaults setValue: [NSNumber numberWithDouble: [newFont pointSize]] forKey: @"fontSize"];
116 125 }
117 126
118 127 @end
16 English.lproj/MainMenu.xib
@@ -2,9 +2,9 @@
2 2 <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
3 3 <data>
4 4 <int key="IBDocument.SystemTarget">1050</int>
5   - <string key="IBDocument.SystemVersion">9G55</string>
  5 + <string key="IBDocument.SystemVersion">9J61</string>
6 6 <string key="IBDocument.InterfaceBuilderVersion">677</string>
7   - <string key="IBDocument.AppKitVersion">949.43</string>
  7 + <string key="IBDocument.AppKitVersion">949.46</string>
8 8 <string key="IBDocument.HIToolboxVersion">353.00</string>
9 9 <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
10 10 <bool key="EncodedWithXMLCoder">YES</bool>
@@ -826,7 +826,7 @@
826 826 <object class="NSTextFieldCell" key="NSCell" id="986725230">
827 827 <int key="NSCellFlags">68288064</int>
828 828 <int key="NSCellFlags2">71304192</int>
829   - <string key="NSContents">Line Spacing:</string>
  829 + <string key="NSContents">Line Spread:</string>
830 830 <reference key="NSSupport" ref="901208874"/>
831 831 <reference key="NSControlView" ref="995010602"/>
832 832 <reference key="NSBackgroundColor" ref="248392606"/>
@@ -873,8 +873,8 @@
873 873 <int key="NSCellFlags">917024</int>
874 874 <int key="NSCellFlags2">0</int>
875 875 <reference key="NSControlView" ref="228139828"/>
876   - <double key="NSMaxValue">1.000000e+02</double>
877   - <double key="NSIncrement">1.000000e+00</double>
  876 + <double key="NSMaxValue">1.000000e+01</double>
  877 + <double key="NSIncrement">1.000000e-01</double>
878 878 <bool key="NSAutorepeat">YES</bool>
879 879 </object>
880 880 </object>
@@ -2237,8 +2237,8 @@
2237 2237 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
2238 2238 <reference ref="9"/>
2239 2239 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
2240   - <string>{{321, 407}, {200, 170}}</string>
2241   - <string>{{321, 407}, {200, 170}}</string>
  2240 + <string>{{337, 443}, {200, 170}}</string>
  2241 + <string>{{337, 443}, {200, 170}}</string>
2242 2242 <boolean value="NO"/>
2243 2243 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
2244 2244 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -2320,7 +2320,7 @@
2320 2320 <bool key="EncodedWithXMLCoder">YES</bool>
2321 2321 <object class="IBPartialClassDescription">
2322 2322 <string key="className">AppController</string>
2323   - <string key="superclassName">NSObject</string>
  2323 + <string key="superclassName">NSResponder</string>
2324 2324 <object class="IBClassDescriptionSource" key="sourceIdentifier">
2325 2325 <string key="majorKey">IBProjectSource</string>
2326 2326 <string key="minorKey">AppController.h</string>
14 English.lproj/TextDocument.xib
@@ -34,7 +34,7 @@
34 34 <object class="NSWindowTemplate" id="275939982">
35 35 <int key="NSWindowStyleMask">15</int>
36 36 <int key="NSWindowBacking">2</int>
37   - <string key="NSWindowRect">{{103, -42}, {939, 787}}</string>
  37 + <string key="NSWindowRect">{{103, -31}, {939, 776}}</string>
38 38 <int key="NSWTFlags">1886912512</int>
39 39 <string key="NSWindowTitle">Window</string>
40 40 <string key="NSWindowClass">NSWindow</string>
@@ -59,12 +59,12 @@
59 59 <object class="NSCustomView" id="793227834">
60 60 <reference key="NSNextResponder" ref="479699895"/>
61 61 <int key="NSvFlags">274</int>
62   - <string key="NSFrameSize">{939, 766}</string>
  62 + <string key="NSFrameSize">{939, 755}</string>
63 63 <reference key="NSSuperview" ref="479699895"/>
64 64 <string key="NSClassName">JJTextView</string>
65 65 </object>
66 66 </object>
67   - <string key="NSFrame">{{1, 1}, {939, 766}}</string>
  67 + <string key="NSFrame">{{1, 1}, {939, 755}}</string>
68 68 <reference key="NSSuperview" ref="707828383"/>
69 69 <reference key="NSNextKeyView" ref="793227834"/>
70 70 <reference key="NSDocView" ref="793227834"/>
@@ -100,7 +100,7 @@
100 100 <double key="NSPercent">5.060241e-01</double>
101 101 </object>
102 102 </object>
103   - <string key="NSFrame">{{-1, 20}, {941, 768}}</string>
  103 + <string key="NSFrame">{{-1, 20}, {941, 757}}</string>
104 104 <reference key="NSSuperview" ref="568628114"/>
105 105 <reference key="NSNextKeyView" ref="479699895"/>
106 106 <int key="NSsFlags">562</int>
@@ -109,7 +109,7 @@
109 109 <reference key="NSContentView" ref="479699895"/>
110 110 </object>
111 111 </object>
112   - <string key="NSFrameSize">{939, 787}</string>
  112 + <string key="NSFrameSize">{939, 776}</string>
113 113 <reference key="NSSuperview"/>
114 114 </object>
115 115 <string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
@@ -263,8 +263,8 @@
263 263 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
264 264 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
265 265 <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
266   - <string>{{389, 266}, {939, 787}}</string>
267   - <string>{{389, 266}, {939, 787}}</string>
  266 + <string>{{389, 80}, {939, 776}}</string>
  267 + <string>{{389, 80}, {939, 776}}</string>
268 268 <integer value="1" id="9"/>
269 269 <string>{{201, 387}, {507, 413}}</string>
270 270 <reference ref="9"/>
2  Info.plist
@@ -42,7 +42,7 @@
42 42 <key>CFBundleSignature</key>
43 43 <string>????</string>
44 44 <key>CFBundleVersion</key>
45   - <string>1.0</string>
  45 + <string>2.0</string>
46 46 <key>NSMainNibFile</key>
47 47 <string>MainMenu</string>
48 48 <key>NSPrincipalClass</key>
14 JJTextView.h
@@ -9,20 +9,16 @@
9 9 #import <Cocoa/Cocoa.h>
10 10
11 11 @interface JJTextView : NSView {
12   - NSSize textContainerInset;
13   - CGFloat lineGap;
  12 + NSSize textInset;
14 13 NSColor *backgroundColor;
15   - NSFont *font;
16 14 NSString *string;
17   - CTFrameRef frame;
  15 + NSMutableArray *textFrames;
18 16 }
19 17
20   -@property (assign) NSSize textContainerInset;
21   -@property (assign) CGFloat lineGap;
  18 +@property (assign) NSSize textInset;
22 19 @property (retain) NSColor *backgroundColor;
23   -@property (retain) NSFont *font;
24   -@property (copy) NSString *string;
25 20
26   -- (void) relayout;
  21 +- (void) setString: (NSString *) string;
  22 +- (void) invalidateLayout;
27 23
28 24 @end
207 JJTextView.m
@@ -8,27 +8,51 @@
8 8
9 9 #import "JJTextView.h"
10 10
  11 +void CTFrameDrawLines(CTFrameRef frame, CGContextRef context)
  12 +{
  13 + CFArrayRef lines = CTFrameGetLines(frame);
  14 + CFIndex i, total = CFArrayGetCount(lines);
  15 + CGPoint origins[255];
  16 + CGRect rect = CGPathGetBoundingBox(CTFrameGetPath(frame));
  17 + CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
  18 +
  19 + // NSLog(@"bounding rect: %@", NSStringFromRect(NSRectFromCGRect(rect)));
  20 + for (i = 0; i < total; i++)
  21 + {
  22 + CTLineRef line = CFArrayGetValueAtIndex(lines, i);
  23 + // NSLog(@"origins[%d].y = %g", i, rect.size.height - origins[i].y);
  24 + CGContextSetTextPosition(context,
  25 + rect.origin.x + origins[i].x,
  26 + rect.origin.y + rect.size.height - origins[i].y);
  27 + CTLineDraw(line, context);
  28 + }
  29 +}
  30 +
11 31 @implementation JJTextView
12 32
13   -@synthesize textContainerInset;
14   -@synthesize lineGap;
  33 +@synthesize textInset;
15 34 @synthesize backgroundColor;
16   -@synthesize font;
17   -@synthesize string;
  35 +
  36 +- (id) initWithFrame: (NSRect) frameRect
  37 +{
  38 + if ((self = [super initWithFrame: frameRect]))
  39 + {
  40 + textInset = NSMakeSize(20, 20);
  41 + textFrames = [[NSMutableArray alloc] init];
  42 + }
  43 + return self;
  44 +}
18 45
19 46 - (void) awakeFromNib
20 47 {
21 48 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
22   - NSArray *keyPaths = [NSArray arrayWithObjects: @"backgroundColor", @"lineHeight", nil];
  49 + NSArray *keyPaths = [NSArray arrayWithObjects: @"backgroundColor", @"lineHeight", @"fontName", @"fontSize", nil];
23 50
24 51 for (NSString *keyPath in keyPaths)
25 52 [defaults addObserver: self
26 53 forKeyPath: keyPath
27 54 options: 0
28 55 context: nil];
29   -
30   - self.textContainerInset = NSMakeSize(20, 20);
31   - self.lineGap = [defaults doubleForKey: @"lineHeight"];
32 56 }
33 57
34 58 - (void) dealloc
@@ -37,45 +61,131 @@ - (void) dealloc
37 61 forKeyPath: @"backgroundColor"];
38 62 [[NSUserDefaults standardUserDefaults] removeObserver: self
39 63 forKeyPath: @"lineHeight"];
  64 + [string release];
  65 + string = nil;
40 66
  67 + [textFrames release];
  68 + textFrames = nil;
  69 +
41 70 [super dealloc];
42 71 }
43 72
44   -- (void) relayout
  73 +- (void) setString: (NSString *) str
45 74 {
46   - NSLog(@"self string: %@", [self string]);
47   - if (string)
  75 + string = [str retain];
  76 + [self invalidateLayout];
  77 +}
  78 +
  79 +- (void) invalidateLayout
  80 +{
  81 + if (! string)
  82 + return;
  83 +
  84 + NSRect rect = [[self enclosingScrollView] documentVisibleRect];
  85 + NSLog(@"rect to draw: %@", NSStringFromRect(rect));
  86 + NSRect newFrame = rect;
  87 + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  88 + CGFloat lineHeight = [defaults doubleForKey: @"lineHeight"];
  89 +
  90 + CTParagraphStyleSetting settings[] = {
  91 + { kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(CGFloat), &lineHeight },
  92 + };
  93 + CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
  94 + NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString: string];
  95 + NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
  96 + [NSFont fontWithName: [defaults stringForKey: @"fontName"]
  97 + size: [defaults doubleForKey: @"fontSize"]],
  98 + (NSString *) kCTFontAttributeName,
  99 + paragraphStyle, (NSString *) kCTParagraphStyleAttributeName,
  100 + nil];
  101 + CFRelease(paragraphStyle);
  102 +
  103 + [attrString setAttributes: attributes
  104 + range: NSMakeRange(0, string.length)];
  105 +
  106 + // Create the framesetter with the attributed string.
  107 + CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef) attrString);
  108 + [attrString release];
  109 +
  110 + CFRange fullRange = CFRangeMake(0, string.length);
  111 + CGRect frameRect = CGRectMake(textInset.width, textInset.height,
  112 + rect.size.width - 2 * textInset.width,
  113 + rect.size.height - textInset.height);
  114 +
  115 + [textFrames removeAllObjects];
  116 + CFRange range, frameRange;
  117 + for (range = frameRange = CFRangeMake(0, 0);
  118 + range.location < fullRange.length;
  119 + range.location += frameRange.length)
48 120 {
49   - // Initialize a rectangular path.
50 121 CGMutablePathRef path = CGPathCreateMutable();
51   - CGPathAddRect(path, NULL, NSRectToCGRect([self frame]));
  122 + CGPathAddRect(path, NULL, frameRect);
52 123
53   - NSAttributedString *attrString = [[NSAttributedString alloc] initWithString: string];
  124 + CTFrameRef frame = CTFramesetterCreateFrame(framesetter, range, path, NULL);
  125 + frameRange = CTFrameGetVisibleStringRange(frame);
  126 +#if 0
  127 + NSLog(@"frameRange: %ld, %ld, %@",
  128 + frameRange.location, frameRange.length,
  129 + NSStringFromRect(NSRectFromCGRect(frameRect)));
  130 +#endif
  131 + // range.location += frameRange.length;
  132 + frameRect.origin.y += frameRect.size.height;
  133 + frameRect.size.height = rect.size.height;
54 134
55   - // Create the framesetter with the attributed string.
56   - CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef) attrString);
57   - [attrString release];
  135 + [textFrames addObject: (id) frame];
58 136
59   - if (frame)
60   - CFRelease(frame);
  137 + CFRelease(path);
  138 + CFRelease(frame);
  139 + }
61 140
62   - // Create the frame and draw it into the graphics context
63   - frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
  141 + CFRelease(framesetter);
64 142
65   - CFRelease(framesetter);
66   - }
  143 + newFrame.size.height = frameRect.origin.y;
  144 + [self setFrame: newFrame];
  145 + [self setNeedsDisplay: YES];
67 146 }
68 147
69   -- (void) drawRect: (NSRect) rect
  148 +- (BOOL) isFlipped
70 149 {
71   - NSLog(@"rect: %@", NSStringFromRect(rect));
  150 + return YES;
  151 +}
72 152
73   - [self relayout];
  153 +- (void) drawRect: (NSRect) rect
  154 +{
  155 + // NSLog(@"rect: %@", NSStringFromRect(rect));
74 156
75 157 // Initialize a graphics context and set the text matrix to a known value.
76 158 CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
77   - CGContextSetTextMatrix(context, CGAffineTransformIdentity);
78   - CTFrameDraw(frame, context);
  159 + CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1, -1));
  160 +
  161 + CTFrameRef frame = (CTFrameRef) [textFrames objectAtIndex: 1];
  162 + if (! frame)
  163 + frame = (CTFrameRef) [textFrames objectAtIndex: 0];
  164 + if (! frame)
  165 + return;
  166 +
  167 + CGRect bounds = CGPathGetBoundingBox(CTFrameGetPath(frame));
  168 + NSUInteger i, start = (rect.origin.y - textInset.height) / bounds.size.height;
  169 +
  170 + for (i = start; i < start + 2 && i < [textFrames count]; i++)
  171 + {
  172 + frame = (CTFrameRef) [textFrames objectAtIndex: i];
  173 +
  174 +#if 0
  175 + NSLog(@"drawing frame: %lu, rect: %@", i, NSStringFromRect(NSRectFromCGRect(bounds)));
  176 + CGRect bounds = CGPathGetBoundingBox(CTFrameGetPath(frame));
  177 +
  178 + CGContextSetRGBFillColor(context, 0.1, 0.7, 0.7, 1.0);
  179 + NSRectFill(NSRectFromCGRect(bounds));
  180 +
  181 + [[NSColor redColor] set];
  182 + NSRectFill(NSMakeRect(200, bounds.origin.y, 50, 1.5));
  183 +
  184 + [[NSColor blackColor] set];
  185 + NSRectFill(NSMakeRect(0, bounds.origin.y + bounds.size.height, 50, 1.5));
  186 +#endif
  187 + CTFrameDrawLines((CTFrameRef) frame, context);
  188 + }
79 189 }
80 190
81 191 - (void) observeValueForKeyPath: (NSString *) keyPath
@@ -88,11 +198,10 @@ - (void) observeValueForKeyPath: (NSString *) keyPath
88 198 if ([keyPath isEqual: @"backgroundColor"])
89 199 self.backgroundColor = [[NSApp delegate] backgroundColor];
90 200
91   - else if ([keyPath isEqual: @"lineHeight"])
92   - {
93   - self.lineGap = [[NSUserDefaults standardUserDefaults] doubleForKey: @"lineHeight"];
94   - [self relayout];
95   - }
  201 + else if ([keyPath isEqual: @"lineHeight"] ||
  202 + [keyPath isEqual: @"fontName"] ||
  203 + [keyPath isEqual: @"fontSize"])
  204 + [self invalidateLayout];
96 205 }
97 206
98 207 - (void) scrollTo: (float) y
@@ -115,6 +224,7 @@ - (void) scrollBy: (float) value
115 224 - (BOOL) processKey: (int) ch
116 225 {
117 226 float y;
  227 + CGFloat pageHeight = [(NSScrollView *) [self superview] documentVisibleRect].size.height;
118 228
119 229 switch (ch)
120 230 {
@@ -128,27 +238,27 @@ - (BOOL) processKey: (int) ch
128 238
129 239 case ' ':
130 240 case NSPageDownFunctionKey:
131   - [self scrollPageDown:self];
  241 + [self scrollBy: pageHeight];
132 242 break;
133 243
134 244 case NSPageUpFunctionKey:
135   - [self scrollPageUp:self];
  245 + [self scrollBy: -pageHeight];
136 246 break;
137 247
138 248 case NSEndFunctionKey:
139 249 y = NSMaxY([[[self enclosingScrollView] documentView] frame]) -
140   - NSHeight([[[self enclosingScrollView] contentView] bounds]);
  250 + NSHeight([[[self enclosingScrollView] contentView] bounds]);
141 251 [self scrollTo: y];
142 252 break;
143   -
  253 +
144 254 case NSHomeFunctionKey:
145   - [self scrollTo:0.0];
  255 + [self scrollTo: 0];
146 256 break;
147   -
  257 +
148 258 default:
149 259 return NO;
150 260 }
151   -
  261 +
152 262 return YES;
153 263 }
154 264
@@ -167,21 +277,4 @@ - (void) keyDown: (NSEvent *) event
167 277 }
168 278 }
169 279
170   -- (BOOL) acceptsFirstResponder
171   -{
172   - return YES;
173   -}
174   -
175   -- (void) changeFont: (id) sender
176   -{
177   - NSFont *oldFont = [self font];
178   - NSFont *newFont = [sender convertFont: oldFont];
179   - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
180   -
181   - NSLog(@"changeFont = %@", newFont);
182   -
183   - [defaults setValue: [newFont fontName] forKey: @"fontName"];
184   - [defaults setValue: [NSNumber numberWithDouble: [newFont pointSize]] forKey: @"fontSize"];
185   -}
186   -
187 280 @end
13 README.markdown
Source Rendered
... ... @@ -0,0 +1,13 @@
  1 +Textus
  2 +======
  3 +
  4 +A new ebook reader on Mac OS X. Powered by Core Text, super fast.
  5 +
  6 +TODO
  7 +----
  8 +
  9 +* Fix last frame height too long problem
  10 +* Use line-scale redraw to further optimize performance
  11 +* Vertical layout
  12 +* On-demand layout (according to pages)
  13 +
4 TextDocument.m
@@ -40,11 +40,7 @@ - (void) windowControllerDidLoadNib: (NSWindowController *) aController
40 40
41 41 [textView setBackgroundColor: [(AppController *) [NSApp delegate] backgroundColor]];
42 42 if (fileContents)
43   - {
44 43 [textView setString: fileContents];
45   - [textView scrollPoint: NSMakePoint(0.0, 0)];
46   - [textView setNeedsDisplay: YES];
47   - }
48 44 }
49 45
50 46 - (NSData *) dataOfType: (NSString *) typeName

0 comments on commit 8c493c8

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