1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
|
/* -*-objc-*-
GSAutoLayoutManager.h
Copyright (C) 2002 Free Software Foundation, Inc.
Author: Nicola Pero <n.pero@mi.flashnet.it>
Date: April 2002
This file is part of GNUstep Renaissance
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _GNUstep_H_GSAutoLayoutManager
#define _GNUstep_H_GSAutoLayoutManager
#ifndef GNUSTEP
# include <Foundation/Foundation.h>
# include <AppKit/AppKit.h>
# include "GNUstep.h"
#else
# include <Foundation/NSObject.h>
#endif
#include "GSAutoLayoutDefaults.h"
/* This design is old and could be changed. */
/*
* There are potentially infinite ways in which you may want to
* arrange your objects in a window. :-)
*
* Renaissance/AutoLayout provides a few classes which should allow
* you to autolayout your objects, with a small effort, in
* most standard cases.
*
* The basic intelligent objects in these classes are the
* GSAutoLayoutManager objects.
*
* There are two main subclasses, GSAutoLayoutStandardManager and
* GSAutoLayoutProportionalManager. You are not supposed to create
* other subclasses, and only rarely to interact with them directly -
* autolayout managers are mainly used internally by the user-level
* objects (boxes and grids).
*
* A single GSAutoLayoutManager performs the basic autolayout
* operations on a line. Basically, it manages a line, and decides
* how to break the line into segments, or how to line up segments in
* order to build up the line. Additionally, the autolayout manager
* can manage multiple lines at the same time, and break those lines
* in segments (/build those lines from segments) in such a way that
* the resulting layout for the different lines are related between
* them: at minimum, all the lines must be of the same total size;
* additional relations between the layout of the lines, depending on
* mutable conditions, can be implemented by subclasses.
* [To understand the requirement of supporting multiple lines, think
* of a table. Each row in the table is a line to be broken in
* columns - the segments; and all rows in the table must be the same
* total size, and must be broken in segments in a similar way - in
* this framework they would share the same autolayout manager)].
*
* Finally, when an autolayout manager has made the layout with
* segments, in each segment it aligns the `segment content' according
* to the border and alignment which was specified for that segment.
*
* We consider all this the primitive autolayout operation, at least for
* our boxes and grids.
*
* GSAutoLayoutManager is an abstract class; its concrete subclasses
* provide different strategies of implementing this primitive
* autolayout operation (eg, GSAutoLayoutStandardManager breaks the
* line into segments of unrelated size - while
* GSAutoLayoutProportionalManager breaks the line into segments of
* equal (or proportional) size).
*
* To manage lines autolayout, a GSAutoLayoutManager needs some
* information about what is to be displayed in the lines. Clients
* (usually box and grid objects) register themselves with the
* GSAutoLayoutManager. A client can register a line, and is given
* back an id, which uniquely identify that line. The client can then
* update the autolayout manager information about that line (and the
* segments contained in that line) at any time. Whenever asked, the
* autolayout manager performs full layout depending on the
* information it has on the lines and segments. When the autolayout
* manager changes the layout of the lines, it posts a
* GSAutoLayoutManagerChangedLayout notification, which the clients
* should observe. Once a client is informed that the layout has
* changed, the client can request to the autolayout manager
* information about the new way its line has been broken into
* segments.
*
* This design is extremely general, but it's not extremely efficient.
* Efficiency was not considered relevant, since in normal conditions
* real window layouts are composed of a few elements (a box normally
* does not contain more than 10 elements).
*
* The GSAutoLayoutManager uses the following information on each
* segment contained in a line:
*
* - each segment on a line is identified by an integer, starting
* from 0 and going up. The only importance of this integer is to
* allow the clients and the autolayout manager to have a way of
* identifying segments on a line, and to specify the sequence of
* segments on the line - segment 0 is always before segment 1 on the
* line etc. Please note that there is no relationship between the
* numbers used in one line and on the other one (that is, you should
* not expect segment 4 on one line to be aligned with segment 4 on
* another line). This number is really an internal identifier used
* between the client and the autolayout manager.
*
* - the left and right border of the segment. These are used so that
* there can be some space around the segment content.
*
* - the minimum size of the segment content.
*
* - the minimum size of the segment - this is computed by summing up
* the left border, the right border, and the minimum size of the
* segment content. The GSAutoLayoutManager, no matter what
* algorithm uses to break the line into segments, should never make
* a segment shorter than this size.
*
* - the alignment type for the segment content. This might either
* be expand, or not expand (in which case there are three variants,
* center, min, and max). We first discuss the expand flag. 0 means
* the segment prefers to stay of its minimum size and not to be
* expanded, while 1 means the segment likes to be expanded (0 is
* used by views where the minimum size already displays all
* available information so there is no point in making the view
* bigger; 1 is used by views where the minimum size is enough to
* display some information, but making the view bigger displays more
* information - so it's good). This flag must be honoured if it's
* 1: ie, the autolayout manager should, whenever possible, try to
* expand segments which have the expand flag set to 1. The
* behaviour when the expand flag is 0 depends on the specific
* autolayout manager. The GSAutoLayoutProportionalManager always
* expands all segments, thus ignoring the flag; the
* GSAutoLayoutStandardManager always expands if possible segments
* with an expand flag of 1, but can't guarantee that segments with
* an expand flag of 0 won't be expanded too (if they are lined up
* with segments with an expand flag of 1 in another line, they will
* be expanded too).
* If the alignment type is not expand (that is, expand is 0), then
* it might either be min, max or center - this is how the segment
* contents are to be aligned inside the segment (after the border
* have been taken into accounts). GSAutoLayoutProportionalManager
* always expands everything and so ignores this alignment type;
* GSAutoLayoutStandardManager instead tries to honour it - if a
* segment is made bigger than its minimum size, the alignment type
* is always honoured when placing the segment contents inside the
* segment.
*
* - a span (a float) for the segment. For a standard layout
* manager, it is the number of columns (assuming eg it's laying out
* in the horizontal direction) that the segment takes up. This is
* very meaningful if there are multiple lines to layout; it's not
* particularly meaningful if there is a single line. A
* GSAutoLayoutProportionalManager interprets it as the number of
* basic units that the segment takes up.
*/
/* This struct is used to store and return layout information for a
* segment. */
typedef struct
{
float position;
float length;
} GSAutoLayoutSegmentLayout;
@class NSMutableSet;
@interface GSAutoLayoutManager : NSObject
{
/* The GSAutoLayoutManagerLine objects, which store the autolayout
* information. */
NSMutableSet *_lines;
/* The minimum length of the lines. */
float _minimumLength;
/* The current length of the lines. */
float _length;
/* If we need to recompute the minimum layout. Set to YES when
* a segment's attribute changes. */
BOOL _needsUpdateMinimumLayout;
/* If we need to recompute the layout. Set to YES when a line's
* forced length is changed. */
BOOL _needsUpdateLayout;
}
/* There are two types of layout an autolayout manager should be able
* to perform.
*
* The first is bottom-to-top autolayout, that is, building lines from
* the composing segments. In this type of autolayout, the autolayout
* manager starts by basically setting all segments to their minimum
* size, and then adjusting this layout (by enlarging segments) to
* meet the constrains (lines of the same length, and other constrains
* imposed by the segment flags and segment relationships). The
* resulting layout is the layout of minimum length which still
* satisfies all constrains (segments are >= their minimum length, all
* lines are of the same size, etc). This layout is the starting
* point of all layout changes - all layout changes are always
* relative to this minimum layout. The autolayout manager
* automatically and always computes and keeps updated this layout
* every time a layout change occurs, because this ideal minimum
* layout is needed as a reference to actually build the actual
* layout. When you first display objects in a window, everything
* is/should normally be displayed by default in this layout, unless a
* frame change is later performed at the user request. Whenever you
* change the attributes of some of the segments (its expand flag, or
* its minimum length, or its span), this minimum autolayout is
* automatically recomputed as soon as you invoke -updateLayout.
*
* The second is top-to-bottom autolayout, that is, breaking lines in
* segments. This type of autolayout is needed when the user acts on
* the window in some way, and this action modifies the layout
* (typically by enlarging or reducing the window size, or by moving a
* splitview divider bar, or similar). In this type of autolayout,
* the autolayout manager is informed that the length of one (or more)
* of its lines has been forced to be a certain fixed amount
* (different than the minimum length). The autolayout manager starts
* by considering the forced lengths of the lines, and searching for
* the minimum forced length; it makes all lines of that length -
* unless that would be less than the minimum line lenght, in which
* case the autolayout manager will simply adopt the minimum length
* layout, refusing to resize the lines it is managing below their
* minimum length - knowing that this means that part of the lines
* will be clipped in the window. The autolayout manager then
* computes the difference between the actual line length and the
* minimum line lenght, and decides how to share the difference in
* length between the different segments.
*
* To cause autolayout to be performed/updated, you call
* -updateLayout. When this method is called, the layout manager
* first calls -internalUpdateMinimumLayout to recompute its minimum
* layout (if any attribute of any segment changed); then, if the
* minimum layout changed, or some other thing requiring the layout to
* be updated happened, the layout manager computes the new line
* length taking into account forced line lenghts. It computes the
* minimum forced line length of all the lines. If the forced length
* is less than the minimum length, the autolayout manager sets the
* line length to _length, but actually uses the minimum layout as the
* layout of views - which means some views are likely going out the
* line! normally that simply results in clipping of them. It then
* calls -internalUpdateLayout to update the layout. If
* -internalUpdateLayout returns YES, it posts an
* GSAutoLayoutManagerChangedLayoutNotification.
*
* You should call this method after you have sent to the autolayout
* manager all updated or new information about the layout you have.
* In a typical session, you first update the layout information by
* calling many times methods adding/removing/modifying segments/lines
* and/or forcing lines to be of certain lengths, then finally you
* perform new layout by calling -updateLayout.
*/
- (void) updateLayout;
/* Subclasses should override this method to update the minimum
* layout. This method is called when the superclass has determined
* that there is a need to update the minimum layout. The subclass
* should recompute the minimum layout from scratch starting from the
* segments and from the segments info (the forced line lengths should
* be ignored). The results of this minimum layout should be stored
* in the _minimumLayout ivar for segments, and in the global
* _minimumLength ivar for the global minimum length of lines. This
* method should return YES if there was a change in the minimum
* layout, and NO if at the end of the recomputation, the minimum
* layout was found to be the same. Returning NO in certain cases
* prevents further useless computations to be done, but it is only
* for efficiency - it's safe to always return YES.
*/
- (BOOL) internalUpdateMinimumLayout;
/* Subclasses should override this method to update the layout. This
* method is called when the superclass has determined that there is a
* need to update the layout, and after the minimum layout has been
* updated if there is a need to, and the new _length that the lines
* must have has been computed. This method is only called if this
* _length is bigger than the _minimumLength. The subclass should
* decide how to distribute the difference between the _length and the
* _minimumLength in each line between the various segments. This
* method should return YES if there was a change in the layout, and
* NO if at the end of the recomputation, the layout was found to be
* the same. Returning NO prevents the notification for changed layout
* to be sent to clients, so it's better to return NO if we can determine
* that no layout change was done. */
- (BOOL) internalUpdateLayout;
/* NB: All the GSAutoLayoutManager methods DO NOT CAUSE ANY AUTOLAYOUT
* UNTIL YOU CALL -updateLayout. */
/* Add a new line to the autolayout manager. The returned id is an
* identifier for that line used in all subsequent communications with
* the layout manager. The line is created with no segments inside.
*/
- (id) addLine;
/* Remove a line from the autolayout manager. */
- (void) removeLine: (id)line;
/* Force the lenght of a line. Normally called to inform the autolayout
* manager of a resizing operated by outside. Use length < 0 to remove
* a forcing on a line. */
- (void) forceLength: (float)length
ofLine: (id)line;
/* Insert a new segment in a line. The segment is inserted at the
* specified index; all following segments are automatically shifted (the
* segment numbers of those segments will change too). */
- (void) insertNewSegmentAtIndex: (int)segment
inLine: (id)line;
/* Remove a segment from a line. All segments following this one will
* be automatically be shifted (the segment number will change
* too). */
- (void) removeSegmentAtIndex: (int)segment
inLine: (id)line;
/* Return the number of segments in that line. */
- (unsigned int) segmentCountInLine: (id)line;
/* Set/read the various autolayout information for segments in a line. */
- (void) setMinimumLength: (float)min
alignment: (GSAutoLayoutAlignment)flag
minBorder: (float)minBorder
maxBorder: (float)maxBorder
span: (float)span
ofSegmentAtIndex: (int)segment
inLine: (id)line;
- (float) minimumLengthOfSegmentAtIndex: (int)segment
inLine: (id)line;
- (GSAutoLayoutAlignment) alignmentOfSegmentAtIndex: (int)segment
inLine: (id)line;
- (float) spanOfSegmentAtIndex: (int)segment
inLine: (id)line;
- (float) minBorderOfSegmentAtIndex: (int)segment
inLine: (id)line;
- (float) maxBorderOfSegmentAtIndex: (int)segment
inLine: (id)line;
/* Read the result of autolayout for a line. The clients should use
* these methods to get the new layout when they receive the
* GSAutoLayoutManagerChangedLayoutNotification. */
- (float) lineLength;
/* This returns the final layout of the segment *contents*. */
- (GSAutoLayoutSegmentLayout) layoutOfSegmentAtIndex: (int)segment
inLine: (id)line;
/* The minimum length of a line in the minimum autolayout. Useful for
* implementing -minimumSizeForContent. */
- (float) minimumLineLength;
@end
extern NSString *GSAutoLayoutManagerChangedLayoutNotification;
#endif
|