Re: Vertically-expanding NSTextField
Re: Vertically-expanding NSTextField
- Subject: Re: Vertically-expanding NSTextField
- From: Sebastian Morsch <email@hidden>
- Date: Sun, 18 Jun 2006 18:02:17 +0200
Dear Andrew,
I was reading this thread because I was looking for a solution for
many views to rearrange in interaction to each other. For example,
one textfields gets bigger because the user types in something and
two other textfields below move downwards accordingly to make room
for it.
Thank you for your code on cocoadev, it was very helpful to write the
automatically expanding textfield! But still I couldn't get other
views to react and move around (which of course wasn't your goal at
all).
So I wrote this protocol to define a system with the following
behaviour: "if a view changes frame, it will rearrange other views to
maintain defined distances between the borders".
The implementation works quite nicely. HOWEVER, this code is veeeery
ruff and newbieish, not very well tested and most probably not very
elegant. I decided to post it anyway for those who like it and
especially for those who don't like it and maybe would like to
improve it. I apologize for the code length! As I said, it still
lacks elegancy and needs improvement.
If someone thinks, that this should go into another or it's own
thread, please let me know!
Thanks!
Sebastian
// START OF SMViewLinking.h
// protocol that views can adopt to maintain their layout as it was
defined in IB.
// one view (source view) can 'link' one of its four graphical
borders (= fix the distance) to those of other
// views (destination views), and those destination views will be
moved/resized if the
// source view changes.
// if the destination views are source views themselves, the linked
destiantion views
// resize, too. this of course, is the big weakness of this system,
because...:
//
// WARNING: you have to avoid 'linkining feedbacks' where subviews
are stiffly linked
// with their superviews in a closed loop. this conflicts with the
standard autoresizing!
#import <Cocoa/Cocoa.h>
// constants
typedef enum {
SMViewLinkingTopBorderType = 1,
SMViewLinkingBottomBorderType = 2,
SMViewLinkingLeftBorderType = 4,
SMViewLinkingRightBorderType = 8
} SMViewLinkingBorderType;
typedef enum {
SMViewLinkingResizesHorizontally = 1,
SMViewLinkingResizesVertically = 2,
SMViewLinkingConstrainedToMinWidth = 4,
SMViewLinkingConstrainedToMinHeight = 8,
SMViewLinkingConstrainedToMaxWidth = 16,
SMViewLinkingConstrainedToMaxHeight = 32,
} SMViewLinkingLinkedResizingMask;
extern NSString *SMViewLinkingDestViewKeyName;
extern NSString *SMViewLinkingSourceBorderKeyName;
extern NSString *SMViewLinkingDestBorderKeyName;
extern NSString *SMViewLinkingDistanceKeyName;
@protocol SMViewLinking
- (void)linkBorder:(SMViewLinkingBorderType)sourceBorder toView:
(NSView *)destView
border:(SMViewLinkingBorderType)destBorder;
- (void)unlinkView:(NSView *)destView;
- (void)moveLinkedBorder:(SMViewLinkingBorderType)border by:(float)
offset;
- (SMViewLinkingLinkedResizingMask)linkedResizingMask;
- (void)setLinkedResizingMask:(SMViewLinkingLinkedResizingMask)mask;
- (NSSize)linkedMinSize;
- (void)setLinkedMinSize:(NSSize)minSize;
- (NSSize)linkedMaxSize;
- (void)setLinkedMaxSize:(NSSize)maxSize;
@end
// helper function
float SMViewLinkingBorderPosition(NSRect frame,
SMViewLinkingBorderType border)
{
if (border == SMViewLinkingTopBorderType) {return (frame.origin.y +
frame.size.height);}
if (border == SMViewLinkingBottomBorderType) {return (frame.origin.y);}
if (border == SMViewLinkingLeftBorderType) {return (frame.origin.x);}
if (border == SMViewLinkingRightBorderType) {return (frame.origin.x
+ frame.size.width);}
return 0.0;
}
// END OF SMViewLinking.h
------------------------------------------------------------------------
---
// START OF SMViewLinking.m
#import "SMViewLinking.h"
// constants
NSString *SMViewLinkingDestViewKeyName =
@"SMViewLinkingDestViewKeyName";
NSString *SMViewLinkingSourceBorderKeyName =
@"SMViewLinkingSourceBorderKeyName";
NSString *SMViewLinkingDestBorderKeyName =
@"SMViewLinkingDestBorderKeyName";
NSString *SMViewLinkingDistanceKeyName =
@"SMViewLinkingDistanceKeyName";
// END OF SMViewLinking.m
------------------------------------------------------------------------
---
// START OF SMLinkedView.h
#import <Cocoa/Cocoa.h>
#import "SMViewLinking.h"
@interface SMLinkedView : NSView <SMViewLinking> {
NSMutableArray *linkedViews;
SMViewLinkingLinkedResizingMask linkedResizingMask;
NSSize linkedMinSize, linkedMaxSize;
}
@end
// END OF SMLinkedView.h
------------------------------------------------------------------------
---
// START OF SMLinkedView.m
#import "SMLinkedView.h"
// private methods
@interface SMLinkedView (private)
- (void)_updateLinkedViews;
- (void)_moveBorder:(SMViewLinkingBorderType)border ofView:(NSView *)
view by:(float)offset;
@end
@implementation SMLinkedView
- (id)initWithFrame:(NSRect)frameRect
{
if (self == [super initWithFrame:frameRect]) {
linkedViews = [[NSMutableArray alloc] initWithCapacity:0];
linkedResizingMask = 0;
linkedMinSize = NSMakeSize(0.0, 0.0);
linkedMaxSize = NSMakeSize(0.0, 0.0);
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder
{
if (self == [super initWithCoder:decoder]) {
linkedViews = [[NSMutableArray alloc] initWithCapacity:0];
linkedResizingMask = 0;
linkedMinSize = NSMakeSize(0.0, 0.0);
linkedMaxSize = NSMakeSize(0.0, 0.0);
}
return self;
}
- (void)dealloc
{
[linkedViews dealloc];
[super dealloc];
}
// SMViewLinking protocol methods
- (void)linkBorder:(SMViewLinkingBorderType)sourceBorder toView:
(NSView *)destView
border:(SMViewLinkingBorderType)destBorder
{
// does the dest view conforms to the SMViewLinking protocol?
if ([[destView class] conformsToProtocol:@protocol(SMViewLinking)]
== NO) {return;}
// determine the current distance
NSRect sourceFrame = [[self superview] convertRect:[self frame]
toView:nil];
// TODO: better (faster ?) transformation!!!
NSRect destFrame = [[destView superview] convertRect:[destView
frame] toView:nil];
// TODO: better (faster ?) transformation!!!
float distance = SMViewLinkingBorderPosition(destFrame, destBorder)
- SMViewLinkingBorderPosition(sourceFrame, sourceBorder);
// add to the linkedViews array
NSDictionary *linkedViewDict = [NSDictionary
dictionaryWithObjectsAndKeys:
destView, SMViewLinkingDestViewKeyName,
[NSNumber numberWithInt:sourceBorder],
SMViewLinkingSourceBorderKeyName,
[NSNumber numberWithInt:destBorder],
SMViewLinkingDestBorderKeyName,
[NSNumber numberWithFloat:distance], SMViewLinkingDistanceKeyName,
nil];
[linkedViews addObject:linkedViewDict];
}
- (void)unlinkView:(NSView *)destView
{
int i;
NSDictionary * linkedViewDict;
for (i = [linkedViews count] - 1; i--; i < 0) {
linkedViewDict = [linkedViews objectAtIndex:i];
if ([linkedViewDict objectForKey:SMViewLinkingDestViewKeyName] ==
destView) {
[linkedViews removeObjectAtIndex:i];
}
}
}
- (void)moveLinkedBorder:(SMViewLinkingBorderType)border by:(float)
offset
{
NSRect frame = [self frame];
// move the border but consider resizing mask
if (border == SMViewLinkingTopBorderType) {
if (linkedResizingMask & SMViewLinkingResizesVertically) {
frame.size.height += offset;
} else {
frame.origin.y += offset;
}
}
if (border == SMViewLinkingBottomBorderType) {
frame.origin.y += offset;
if (linkedResizingMask & SMViewLinkingResizesVertically) {
frame.size.height -= offset;
}
}
if (border == SMViewLinkingLeftBorderType) {
frame.origin.x += offset;
if (linkedResizingMask & SMViewLinkingResizesHorizontally) {
frame.size.width -= offset;
}
}
if (border == SMViewLinkingRightBorderType) {
if (linkedResizingMask & SMViewLinkingResizesHorizontally) {
frame.size.width += offset;
} else {
frame.origin.x += offset;
}
}
// check min/max size
if ((linkedResizingMask & SMViewLinkingConstrainedToMinWidth)
&& (frame.size.width < linkedMinSize.width))
{
frame.size.width = linkedMinSize.width;
}
if ((linkedResizingMask & SMViewLinkingConstrainedToMinHeight)
&& (frame.size.height < linkedMinSize.height))
{
frame.size.height = linkedMinSize.height;
}
if ((linkedResizingMask & SMViewLinkingConstrainedToMaxWidth)
&& (frame.size.width > linkedMaxSize.width))
{
frame.size.width = linkedMaxSize.width;
}
if ((linkedResizingMask & SMViewLinkingConstrainedToMaxHeight)
&& (frame.size.height > linkedMaxSize.height))
{
frame.size.height = linkedMaxSize.height;
}
[self setFrame:frame];
[[self superview] setNeedsDisplay:YES];
}
- (SMViewLinkingLinkedResizingMask)linkedResizingMask {
return linkedResizingMask;
}
- (void)setLinkedResizingMask:(SMViewLinkingLinkedResizingMask)mask {
linkedResizingMask = mask;
}
- (NSSize)linkedMinSize {
return linkedMinSize;
}
- (void)setLinkedMinSize:(NSSize)minSize {
linkedMinSize = minSize;
}
- (NSSize)linkedMaxSize {
return linkedMaxSize;
}
- (void)setLinkedMaxSize:(NSSize)maxSize {
linkedMaxSize = maxSize;
}
// overridden and private methods
- (void)setFrame:(NSRect)frameRect
{
[super setFrame:frameRect];
[self _updateLinkedViews];
}
- (void)_updateLinkedViews
{
// loop thru all linked views and see if they have to change
NSEnumerator *enumerator = [linkedViews objectEnumerator];
NSDictionary *linkedViewDict;
id destView;
NSRect sourceFrame = [[self superview] convertRect:[self frame]
toView:nil];
// TODO: better (faster ?) transformation!!!
NSRect destFrame;
SMViewLinkingBorderType sourceBorder, destBorder;
float linkedDistance, actualDistance;
while (linkedViewDict = [enumerator nextObject])
{
destView = [linkedViewDict objectForKey:SMViewLinkingDestViewKeyName];
destFrame = [[destView superview] convertRect:[destView frame]
toView:nil];
// TODO: better (faster ?) transformation!!!
sourceBorder = [[linkedViewDict
objectForKey:SMViewLinkingSourceBorderKeyName] intValue];
destBorder = [[linkedViewDict
objectForKey:SMViewLinkingDestBorderKeyName] intValue];
linkedDistance = [[linkedViewDict
objectForKey:SMViewLinkingDistanceKeyName] floatValue];
actualDistance = SMViewLinkingBorderPosition(destFrame, destBorder)
- SMViewLinkingBorderPosition(sourceFrame, sourceBorder);
if (actualDistance != linkedDistance) {
[destView moveLinkedBorder:destBorder by:(linkedDistance -
actualDistance)];
}
}
}
@end
// END OF SMLinkedView.m
Am 13.05.2006 um 12:26 schrieb Andrew Bowman:
Okay, I've been pounding away at this for a while and have a
working solution posted at CocoaDev.
http://www.cocoadev.com/index.pl?IFVerticallyExpandingTextfield
Take a look! It mimics iChat's input field quite well.
Comments and suggestions welcome.
- Andrew Bowman
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden