git.y1.nz

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.