SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
HexFiend/HFLayoutRepresenter.m
1 //
2 // HFRepresenterLayoutView.m
3 // HexFiend_2
4 //
5 // Copyright 2007 ridiculous_fish. All rights reserved.
6 //
7
8 #import <HexFiend/HFLayoutRepresenter.h>
9
10 @interface HFRepresenterLayoutViewInfo : NSObject {
11 @public
12 HFRepresenter *rep;
13 NSView *view;
14 NSPoint layoutPosition;
15 NSRect frame;
16 NSUInteger autoresizingMask;
17 }
18
19 @end
20
21 @implementation HFRepresenterLayoutViewInfo
22
23 - (NSString *)description {
24 return [NSString stringWithFormat:@"<%@ : %@>", view, NSStringFromRect(frame)];
25 }
26
27 @end
28
29 @implementation HFLayoutRepresenter
30
31 static NSInteger sortByLayoutPosition(id a, id b, void *self) {
32 USE(self);
33 NSPoint pointA = [a layoutPosition];
34 NSPoint pointB = [b layoutPosition];
35 if (pointA.y < pointB.y) return -1;
36 else if (pointA.y > pointB.y) return 1;
37 else if (pointA.x < pointB.x) return -1;
38 else if (pointA.x > pointB.x) return 1;
39 else return 0;
40 }
41
42 - (NSArray *)arraysOfLayoutInfos {
43 if (! representers) return nil;
44
45 NSMutableArray *result = [NSMutableArray array];
46 NSArray *reps = [representers sortedArrayUsingFunction:sortByLayoutPosition context:self];
47 NSMutableArray *currentReps = [NSMutableArray array];
48 CGFloat currentRepY = - CGFLOAT_MAX;
49 FOREACH(HFRepresenter*, rep, reps) {
50 HFRepresenterLayoutViewInfo *info = [[HFRepresenterLayoutViewInfo alloc] init];
51 info->rep = rep;
52 info->view = [rep view];
53 info->frame = [info->view frame];
54 info->layoutPosition = [rep layoutPosition];
55 info->autoresizingMask = [info->view autoresizingMask];
56 if (info->layoutPosition.y != currentRepY && [currentReps count] > 0) {
57 [result addObject:[[currentReps copy] autorelease]];
58 [currentReps removeAllObjects];
59 }
60 currentRepY = info->layoutPosition.y;
61 [currentReps addObject:info];
62 [info release];
63 }
64 if ([currentReps count]) [result addObject:[[currentReps copy] autorelease]];
65 return result;
66 }
67
68 - (NSRect)boundsRectForLayout {
69 NSRect result = [[self view] bounds];
70 /* Sometimes when we are not yet in a window, we get wonky bounds, so be paranoid. */
71 if (result.size.width < 0 || result.size.height < 0) result = NSZeroRect;
72 return result;
73 }
74
75 - (CGFloat)_computeMinHeightForLayoutInfos:(NSArray *)infos {
76 CGFloat result = 0;
77 HFASSERT(infos != NULL);
78 HFASSERT([infos count] > 0);
79 FOREACH(HFRepresenterLayoutViewInfo *, info, infos) {
80 if (! (info->autoresizingMask & NSViewHeightSizable)) result = MAX(result, NSHeight([info->view frame]));
81 }
82 return result;
83 }
84
85 - (void)_applyYLocation:(CGFloat)yLocation andMinHeight:(CGFloat)height toInfos:(NSArray *)layoutInfos {
86 FOREACH(HFRepresenterLayoutViewInfo *, info, layoutInfos) {
87 info->frame.origin.y = yLocation;
88 if (info->autoresizingMask & NSViewHeightSizable) info->frame.size.height = height;
89 }
90 }
91
92 - (void)_layoutInfosHorizontally:(NSArray *)infos inRect:(NSRect)layoutRect withBytesPerLine:(NSUInteger)bytesPerLine {
93 CGFloat nextX = NSMinX(layoutRect);
94 NSUInteger numHorizontallyResizable = 0;
95 FOREACH(HFRepresenterLayoutViewInfo *, info, infos) {
96 CGFloat minWidth = [info->rep minimumViewWidthForBytesPerLine:bytesPerLine];
97 info->frame.origin.x = nextX;
98 info->frame.size.width = minWidth;
99 nextX += minWidth;
100 numHorizontallyResizable += !! (info->autoresizingMask & NSViewWidthSizable);
101 }
102
103 CGFloat remainingWidth = NSMaxX(layoutRect) - nextX;
104 if (numHorizontallyResizable > 0 && remainingWidth > 0) {
105 NSView *view = [self view];
106 CGFloat remainingPixels = [view convertSize:NSMakeSize(remainingWidth, 0) toView:nil].width;
107 HFASSERT(remainingPixels > 0);
108 CGFloat pixelsPerView = HFFloor(HFFloor(remainingPixels) / (CGFloat)numHorizontallyResizable);
109 if (pixelsPerView > 0) {
110 CGFloat pointsPerView = [view convertSize:NSMakeSize(pixelsPerView, 0) fromView:nil].width;
111 CGFloat pointsAdded = 0;
112 FOREACH(HFRepresenterLayoutViewInfo *, info, infos) {
113 info->frame.origin.x += pointsAdded;
114 if (info->autoresizingMask & NSViewWidthSizable) {
115 info->frame.size.width += pointsPerView;
116 pointsAdded += pointsPerView;
117 }
118 }
119 }
120 }
121 }
122
123 - (CGFloat)minimumViewWidthForBytesPerLine:(NSUInteger)bytesPerLine {
124 CGFloat result = 0;
125 NSArray *arraysOfLayoutInfos = [self arraysOfLayoutInfos];
126
127 FOREACH(NSArray *, layoutInfos, arraysOfLayoutInfos) {
128 CGFloat minWidthForRow = 0;
129 FOREACH(HFRepresenterLayoutViewInfo *, info, layoutInfos) {
130 minWidthForRow += [info->rep minimumViewWidthForBytesPerLine:bytesPerLine];
131 }
132 result = MAX(result, minWidthForRow);
133 }
134 return result;
135 }
136
137 - (NSUInteger)_computeBytesPerLineForArraysOfLayoutInfos:(NSArray *)arraysOfLayoutInfos forLayoutInRect:(NSRect)layoutRect {
138 /* The granularity is our own granularity (probably 1), LCMed with the granularities of all other representers */
139 NSUInteger granularity = [self byteGranularity];
140 FOREACH(HFRepresenter *, representer, representers) {
141 granularity = HFLeastCommonMultiple(granularity, [representer byteGranularity]);
142 }
143 HFASSERT(granularity >= 1);
144
145 NSUInteger newNumGranules = (NSUIntegerMax - 1) / granularity;
146 FOREACH(NSArray *, layoutInfos, arraysOfLayoutInfos) {
147 NSUInteger maxKnownGood = 0, minKnownBad = newNumGranules + 1;
148 while (maxKnownGood + 1 < minKnownBad) {
149 CGFloat requiredSpace = 0;
150 NSUInteger proposedNumGranules = maxKnownGood + (minKnownBad - maxKnownGood)/2;
151 NSUInteger proposedBytesPerLine = proposedNumGranules * granularity;
152 FOREACH(HFRepresenterLayoutViewInfo *, info, layoutInfos) {
153 requiredSpace += [info->rep minimumViewWidthForBytesPerLine:proposedBytesPerLine];
154 if (requiredSpace > NSWidth(layoutRect)) break;
155 }
156 if (requiredSpace > NSWidth(layoutRect)) minKnownBad = proposedNumGranules;
157 else maxKnownGood = proposedNumGranules;
158 }
159 newNumGranules = maxKnownGood;
160 }
161 return MAX(1u, newNumGranules) * granularity;
162 }
163
164 - (BOOL)_anyLayoutInfoIsVerticallyResizable:(NSArray *)vals {
165 HFASSERT(vals != NULL);
166 FOREACH(HFRepresenterLayoutViewInfo *, info, vals) {
167 if (info->autoresizingMask & NSViewHeightSizable) return YES;
168 }
169 return NO;
170 }
171
172 - (BOOL)_addVerticalHeight:(CGFloat)heightPoints andOffset:(CGFloat)offsetPoints toLayoutInfos:(NSArray *)layoutInfos {
173 BOOL isVerticallyResizable = [self _anyLayoutInfoIsVerticallyResizable:layoutInfos];
174 CGFloat totalHeight = [self _computeMinHeightForLayoutInfos:layoutInfos] + heightPoints;
175 FOREACH(HFRepresenterLayoutViewInfo *, info, layoutInfos) {
176 info->frame.origin.y += offsetPoints;
177 if (isVerticallyResizable) {
178 if (info->autoresizingMask & NSViewHeightSizable) {
179 info->frame.size.height = totalHeight;
180 }
181 else {
182 CGFloat diff = totalHeight - info->frame.size.height;
183 HFASSERT(diff >= 0);
184 info->frame.origin.y += HFFloor(diff);
185 }
186 }
187 }
188 return isVerticallyResizable;
189 }
190
191 - (void)_distributeVerticalSpace:(CGFloat)space toArraysOfLayoutInfos:(NSArray *)arraysOfLayoutInfos {
192 HFASSERT(space >= 0);
193 HFASSERT(arraysOfLayoutInfos != NULL);
194
195 NSUInteger consumers = 0;
196 FOREACH(NSArray *, layoutInfos, arraysOfLayoutInfos) {
197 if ([self _anyLayoutInfoIsVerticallyResizable:layoutInfos]) consumers++;
198 }
199 if (consumers > 0) {
200 NSView *view = [self view];
201 CGFloat availablePixels = [view convertSize:NSMakeSize(0, space) toView:nil].height;
202 HFASSERT(availablePixels > 0);
203 CGFloat pixelsPerView = HFFloor(HFFloor(availablePixels) / (CGFloat)consumers);
204 CGFloat pointsPerView = [view convertSize:NSMakeSize(0, pixelsPerView) fromView:nil].height;
205 CGFloat yOffset = 0;
206 if (pointsPerView > 0) {
207 FOREACH(NSArray *, layoutInfos, arraysOfLayoutInfos) {
208 if ([self _addVerticalHeight:pointsPerView andOffset:yOffset toLayoutInfos:layoutInfos]) {
209 yOffset += pointsPerView;
210 }
211 }
212 }
213 }
214 }
215
216 - (void)performLayout {
217 HFController *controller = [self controller];
218 if (! controller) return;
219 if (! representers) return;
220
221 NSArray *arraysOfLayoutInfos = [self arraysOfLayoutInfos];
222 if (! [arraysOfLayoutInfos count]) return;
223
224 NSUInteger transaction = [controller beginPropertyChangeTransaction];
225
226 NSRect layoutRect = [self boundsRectForLayout];
227
228 NSUInteger bytesPerLine;
229 if (maximizesBytesPerLine) bytesPerLine = [self _computeBytesPerLineForArraysOfLayoutInfos:arraysOfLayoutInfos forLayoutInRect:layoutRect];
230 else bytesPerLine = [controller bytesPerLine];
231
232 CGFloat yPosition = NSMinY(layoutRect);
233 FOREACH(NSArray *, layoutInfos, arraysOfLayoutInfos) {
234 HFASSERT([layoutInfos count] > 0);
235 CGFloat minHeight = [self _computeMinHeightForLayoutInfos:layoutInfos];
236 [self _applyYLocation:yPosition andMinHeight:minHeight toInfos:layoutInfos];
237 yPosition += minHeight;
238 [self _layoutInfosHorizontally:layoutInfos inRect:layoutRect withBytesPerLine:bytesPerLine];
239 }
240
241 CGFloat remainingVerticalSpace = NSMaxY(layoutRect) - yPosition;
242 if (remainingVerticalSpace > 0) {
243 [self _distributeVerticalSpace:remainingVerticalSpace toArraysOfLayoutInfos:arraysOfLayoutInfos];
244 }
245
246 FOREACH(NSArray *, layoutInfoArray, arraysOfLayoutInfos) {
247 FOREACH(HFRepresenterLayoutViewInfo *, info, layoutInfoArray) {
248 [info->view setFrame:info->frame];
249 }
250 }
251
252 [controller endPropertyChangeTransaction:transaction];
253 }
254
255 - (NSArray *)representers {
256 return representers ? [[representers copy] autorelease] : @[];
257 }
258
259 - (instancetype)init {
260 self = [super init];
261 maximizesBytesPerLine = YES;
262 return self;
263 }
264
265 - (void)dealloc {
266 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewFrameDidChangeNotification object:[self view]];
267 [representers release];
268 [super dealloc];
269 }
270
271 - (void)encodeWithCoder:(NSCoder *)coder {
272 HFASSERT([coder allowsKeyedCoding]);
273 [super encodeWithCoder:coder];
274 [coder encodeObject:representers forKey:@"HFRepresenters"];
275 [coder encodeBool:maximizesBytesPerLine forKey:@"HFMaximizesBytesPerLine"];
276 }
277
278 - (instancetype)initWithCoder:(NSCoder *)coder {
279 HFASSERT([coder allowsKeyedCoding]);
280 self = [super initWithCoder:coder];
281 representers = [[coder decodeObjectForKey:@"HFRepresenters"] retain];
282 maximizesBytesPerLine = [coder decodeBoolForKey:@"HFMaximizesBytesPerLine"];
283 NSView *view = [self view];
284 [view setPostsFrameChangedNotifications:YES];
285 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameChanged:) name:NSViewFrameDidChangeNotification object:view];
286 return self;
287 }
288
289 - (void)addRepresenter:(HFRepresenter *)representer {
290 REQUIRE_NOT_NULL(representer);
291 if (! representers) representers = [[NSMutableArray alloc] init];
292 HFASSERT([representers indexOfObjectIdenticalTo:representer] == NSNotFound);
293 [representers addObject:representer];
294 HFASSERT([[representer view] superview] != [self view]);
295 [[self view] addSubview:[representer view]];
296 [self performLayout];
297 }
298
299 - (void)removeRepresenter:(HFRepresenter *)representer {
300 REQUIRE_NOT_NULL(representer);
301 HFASSERT([representers indexOfObjectIdenticalTo:representer] != NSNotFound);
302 NSView *view = [representer view];
303 HFASSERT([view superview] == [self view]);
304 [view removeFromSuperview];
305 [representers removeObjectIdenticalTo:representer];
306 [self performLayout];
307 }
308
309 - (void)frameChanged:(NSNotification *)note {
310 USE(note);
311 [self performLayout];
312 }
313
314 - (void)initializeView {
315 NSView *view = [self view];
316 [view setPostsFrameChangedNotifications:YES];
317 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(frameChanged:) name:NSViewFrameDidChangeNotification object:view];
318 }
319
320 - (NSView *)createView {
321 return [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
322 }
323
324 - (void)setMaximizesBytesPerLine:(BOOL)val {
325 maximizesBytesPerLine = val;
326 }
327
328 - (BOOL)maximizesBytesPerLine {
329 return maximizesBytesPerLine;
330 }
331
332 - (NSUInteger)maximumBytesPerLineForLayoutInProposedWidth:(CGFloat)proposedWidth {
333 NSArray *arraysOfLayoutInfos = [self arraysOfLayoutInfos];
334 if (! [arraysOfLayoutInfos count]) return 0;
335
336 NSRect layoutRect = [self boundsRectForLayout];
337 layoutRect.size.width = proposedWidth;
338
339 NSUInteger bytesPerLine = [self _computeBytesPerLineForArraysOfLayoutInfos:arraysOfLayoutInfos forLayoutInRect:layoutRect];
340 return bytesPerLine;
341 }
342
343 - (CGFloat)minimumViewWidthForLayoutInProposedWidth:(CGFloat)proposedWidth {
344 NSUInteger bytesPerLine;
345 if ([self maximizesBytesPerLine]) {
346 bytesPerLine = [self maximumBytesPerLineForLayoutInProposedWidth:proposedWidth];
347 } else {
348 bytesPerLine = [[self controller] bytesPerLine];
349 }
350 CGFloat newWidth = [self minimumViewWidthForBytesPerLine:bytesPerLine];
351 return newWidth;
352 }
353
354 - (void)controllerDidChange:(HFControllerPropertyBits)bits {
355 [super controllerDidChange:bits];
356 if (bits & (HFControllerViewSizeRatios | HFControllerBytesPerColumn | HFControllerByteGranularity)) {
357 [self performLayout];
358 }
359 }
360
361 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.