2015-09-10 13:27:45 -05:00
|
|
|
// 9 september 2015
|
2015-10-09 11:33:45 -05:00
|
|
|
#import "uipriv_darwin.h"
|
2015-09-10 13:27:45 -05:00
|
|
|
|
2015-09-10 17:32:16 -05:00
|
|
|
// We are basically cloning NSScrollView here, managing scrolling ourselves.
|
|
|
|
// TODOs
|
|
|
|
// - is the page increment set up right?
|
|
|
|
// - do we need to draw anything in the empty corner?
|
2015-10-09 11:34:26 -05:00
|
|
|
// - autohiding scrollbars
|
2015-09-10 17:32:16 -05:00
|
|
|
|
2015-09-10 13:27:45 -05:00
|
|
|
// NSScrollers have no intrinsic size; here we give it one
|
|
|
|
@interface areaScroller : NSScroller {
|
|
|
|
BOOL libui_vertical;
|
|
|
|
}
|
|
|
|
- (id)initWithFrame:(NSRect)r vertical:(BOOL)v;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation areaScroller
|
|
|
|
|
|
|
|
- (id)initWithFrame:(NSRect)r vertical:(BOOL)v
|
|
|
|
{
|
|
|
|
self = [super initWithFrame:r];
|
|
|
|
if (self)
|
|
|
|
self->libui_vertical = v;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSSize)intrinsicContentSize
|
|
|
|
{
|
|
|
|
NSSize s;
|
|
|
|
CGFloat scrollerWidth;
|
|
|
|
|
|
|
|
s = [super intrinsicContentSize];
|
|
|
|
scrollerWidth = [NSScroller scrollerWidthForControlSize:[self controlSize]
|
|
|
|
scrollerStyle:[self scrollerStyle]];
|
|
|
|
if (self->libui_vertical)
|
|
|
|
s.width = scrollerWidth;
|
|
|
|
else
|
|
|
|
s.height = scrollerWidth;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setControlSize:(NSControlSize)size
|
|
|
|
{
|
|
|
|
[super setControlSize:size];
|
|
|
|
[self invalidateIntrinsicContentSize];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setScrollerStyle:(NSScrollerStyle)style
|
|
|
|
{
|
|
|
|
[super setScrollerStyle:style];
|
|
|
|
[self invalidateIntrinsicContentSize];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface areaDrawingView : NSView {
|
|
|
|
uiArea *libui_a;
|
|
|
|
}
|
|
|
|
- (id)initWithFrame:(NSRect)r area:(uiArea *)a;
|
2015-09-13 18:26:40 -05:00
|
|
|
- (uiModifiers)parseModifiers:(NSEvent *)e;
|
2015-09-11 20:20:49 -05:00
|
|
|
- (void)doMouseEvent:(NSEvent *)e;
|
2015-09-13 18:26:40 -05:00
|
|
|
- (int)sendKeyEvent:(uiAreaKeyEvent *)ke;
|
|
|
|
- (int)doKeyDownUp:(NSEvent *)e up:(int)up;
|
|
|
|
- (int)doKeyDown:(NSEvent *)e;
|
|
|
|
- (int)doKeyUp:(NSEvent *)e;
|
|
|
|
- (int)doFlagsChanged:(NSEvent *)e;
|
2015-09-10 13:27:45 -05:00
|
|
|
@end
|
|
|
|
|
|
|
|
@interface areaView : NSView {
|
|
|
|
uiArea *libui_a;
|
|
|
|
areaDrawingView *drawingView;
|
|
|
|
areaScroller *hscrollbar;
|
|
|
|
areaScroller *vscrollbar;
|
2015-09-10 16:56:09 -05:00
|
|
|
intmax_t hscrollpos;
|
|
|
|
intmax_t vscrollpos;
|
2015-09-10 13:27:45 -05:00
|
|
|
}
|
|
|
|
- (id)initWithFrame:(NSRect)r area:(uiArea *)a;
|
2015-09-10 16:56:09 -05:00
|
|
|
- (void)dvFrameSizeChanged:(NSNotification *)note;
|
|
|
|
- (IBAction)hscrollEvent:(id)sender;
|
|
|
|
- (IBAction)vscrollEvent:(id)sender;
|
2015-09-11 20:20:49 -05:00
|
|
|
- (intmax_t)hscrollPos;
|
|
|
|
- (intmax_t)vscrollPos;
|
2015-09-10 16:56:09 -05:00
|
|
|
// scroll utilities
|
|
|
|
- (intmax_t)hpagesize;
|
|
|
|
- (intmax_t)vpagesize;
|
|
|
|
- (intmax_t)hscrollmax;
|
|
|
|
- (intmax_t)vscrollmax;
|
|
|
|
- (intmax_t)hscrollbarPosition;
|
|
|
|
- (intmax_t)vscrollbarPosition;
|
|
|
|
- (void)hscrollTo:(intmax_t)pos;
|
|
|
|
- (void)vscrollTo:(intmax_t)pos;
|
2015-09-10 13:27:45 -05:00
|
|
|
@end
|
|
|
|
|
|
|
|
struct uiArea {
|
2015-10-09 11:33:45 -05:00
|
|
|
uiDarwinControl c;
|
2015-09-10 13:27:45 -05:00
|
|
|
areaView *view;
|
|
|
|
uiAreaHandler *ah;
|
|
|
|
};
|
|
|
|
|
2015-10-09 11:33:45 -05:00
|
|
|
uiDarwinDefineControl(
|
|
|
|
uiArea, // type name
|
|
|
|
uiAreaType, // type function
|
|
|
|
view // handle
|
|
|
|
)
|
|
|
|
|
2015-09-10 13:27:45 -05:00
|
|
|
@implementation areaDrawingView
|
|
|
|
|
|
|
|
- (id)initWithFrame:(NSRect)r area:(uiArea *)a
|
|
|
|
{
|
|
|
|
self = [super initWithFrame:r];
|
|
|
|
if (self)
|
|
|
|
self->libui_a = a;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)drawRect:(NSRect)r
|
|
|
|
{
|
|
|
|
CGContextRef c;
|
|
|
|
uiAreaDrawParams dp;
|
2015-09-13 18:26:40 -05:00
|
|
|
areaView *av;
|
2015-09-10 13:27:45 -05:00
|
|
|
|
|
|
|
c = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
|
|
|
|
dp.Context = newContext(c);
|
|
|
|
|
2015-10-14 07:50:56 -05:00
|
|
|
// TODO frame or bounds?
|
2015-09-10 13:27:45 -05:00
|
|
|
dp.ClientWidth = [self frame].size.width;
|
|
|
|
dp.ClientHeight = [self frame].size.height;
|
|
|
|
|
|
|
|
dp.ClipX = r.origin.x;
|
|
|
|
dp.ClipY = r.origin.y;
|
|
|
|
dp.ClipWidth = r.size.width;
|
|
|
|
dp.ClipHeight = r.size.height;
|
|
|
|
|
2015-09-13 18:26:40 -05:00
|
|
|
av = (areaView *) [self superview];
|
|
|
|
dp.HScrollPos = [av hscrollPos];
|
|
|
|
dp.VScrollPos = [av vscrollPos];
|
2015-09-10 13:27:45 -05:00
|
|
|
|
2015-10-11 12:53:47 -05:00
|
|
|
// no need to save or restore the graphics state to reset transformations; Cocoa creates a brand-new context each time
|
2015-09-10 13:27:45 -05:00
|
|
|
(*(self->libui_a->ah->Draw))(self->libui_a->ah, self->libui_a, &dp);
|
2015-10-09 11:33:45 -05:00
|
|
|
|
|
|
|
freeContext(dp.Context);
|
2015-09-10 13:27:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isFlipped
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2015-09-11 20:20:49 -05:00
|
|
|
- (BOOL)acceptsFirstResponder
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2015-09-13 18:26:40 -05:00
|
|
|
- (uiModifiers)parseModifiers:(NSEvent *)e
|
|
|
|
{
|
|
|
|
NSEventModifierFlags mods;
|
|
|
|
uiModifiers m;
|
|
|
|
|
|
|
|
m = 0;
|
|
|
|
mods = [e modifierFlags];
|
|
|
|
if ((mods & NSControlKeyMask) != 0)
|
|
|
|
m |= uiModifierCtrl;
|
|
|
|
if ((mods & NSAlternateKeyMask) != 0)
|
|
|
|
m |= uiModifierAlt;
|
|
|
|
if ((mods & NSShiftKeyMask) != 0)
|
|
|
|
m |= uiModifierShift;
|
|
|
|
if ((mods & NSCommandKeyMask) != 0)
|
|
|
|
m |= uiModifierSuper;
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2015-09-11 20:24:39 -05:00
|
|
|
// capture on drag is done automatically on OS X
|
2015-09-11 20:20:49 -05:00
|
|
|
- (void)doMouseEvent:(NSEvent *)e
|
|
|
|
{
|
|
|
|
uiAreaMouseEvent me;
|
|
|
|
NSPoint point;
|
|
|
|
areaView *av;
|
|
|
|
uintmax_t buttonNumber;
|
|
|
|
NSUInteger pmb;
|
|
|
|
unsigned int i, max;
|
|
|
|
|
|
|
|
av = (areaView *) [self superview];
|
|
|
|
|
2015-10-14 07:50:56 -05:00
|
|
|
// this will convert point to drawing space
|
|
|
|
// thanks swillits in irc.freenode.net/#macdev
|
2015-09-11 20:20:49 -05:00
|
|
|
point = [self convertPoint:[e locationInWindow] fromView:nil];
|
|
|
|
me.X = point.x;
|
|
|
|
me.Y = point.y;
|
2015-09-11 20:24:39 -05:00
|
|
|
|
2015-10-14 07:50:56 -05:00
|
|
|
// TODO frame or bounds?
|
2015-09-11 20:24:39 -05:00
|
|
|
me.ClientWidth = [self frame].size.width;
|
|
|
|
me.ClientHeight = [self frame].size.height;
|
2015-09-11 20:20:49 -05:00
|
|
|
me.HScrollPos = [av hscrollPos];
|
|
|
|
me.VScrollPos = [av vscrollPos];
|
|
|
|
|
|
|
|
buttonNumber = [e buttonNumber] + 1;
|
|
|
|
// swap button numbers 2 and 3 (right and middle)
|
|
|
|
if (buttonNumber == 2)
|
|
|
|
buttonNumber = 3;
|
|
|
|
else if (buttonNumber == 3)
|
|
|
|
buttonNumber = 2;
|
|
|
|
|
|
|
|
me.Down = 0;
|
|
|
|
me.Up = 0;
|
|
|
|
me.Count = 0;
|
|
|
|
switch ([e type]) {
|
|
|
|
case NSLeftMouseDown:
|
|
|
|
case NSRightMouseDown:
|
|
|
|
case NSOtherMouseDown:
|
|
|
|
me.Down = buttonNumber;
|
|
|
|
me.Count = [e clickCount];
|
|
|
|
break;
|
|
|
|
case NSLeftMouseUp:
|
|
|
|
case NSRightMouseUp:
|
|
|
|
case NSOtherMouseUp:
|
|
|
|
me.Up = buttonNumber;
|
|
|
|
break;
|
|
|
|
case NSLeftMouseDragged:
|
|
|
|
case NSRightMouseDragged:
|
|
|
|
case NSOtherMouseDragged:
|
|
|
|
// we include the button that triggered the dragged event in the Held fields
|
|
|
|
buttonNumber = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-09-13 18:26:40 -05:00
|
|
|
me.Modifiers = [self parseModifiers:e];
|
2015-09-11 20:20:49 -05:00
|
|
|
|
|
|
|
pmb = [NSEvent pressedMouseButtons];
|
|
|
|
me.Held1To64 = 0;
|
|
|
|
if (buttonNumber != 1 && (pmb & 1) != 0)
|
|
|
|
me.Held1To64 |= 1;
|
|
|
|
if (buttonNumber != 2 && (pmb & 4) != 0)
|
|
|
|
me.Held1To64 |= 2;
|
|
|
|
if (buttonNumber != 3 && (pmb & 2) != 0)
|
|
|
|
me.Held1To64 |= 4;
|
|
|
|
// buttons 4..64
|
|
|
|
max = 32;
|
|
|
|
// TODO are the upper 32 bits just mirrored?
|
|
|
|
// if (sizeof (NSUInteger) == 8)
|
|
|
|
// max = 64;
|
|
|
|
for (i = 4; i <= max; i++) {
|
|
|
|
uint64_t j;
|
|
|
|
|
|
|
|
if (buttonNumber == i)
|
|
|
|
continue;
|
|
|
|
j = 1 << (i - 1);
|
|
|
|
if ((pmb & j) != 0)
|
|
|
|
me.Held1To64 |= j;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*(self->libui_a->ah->MouseEvent))(self->libui_a->ah, self->libui_a, &me);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define mouseEvent(name) \
|
|
|
|
- (void)name:(NSEvent *)e \
|
|
|
|
{ \
|
|
|
|
[self doMouseEvent:e]; \
|
|
|
|
}
|
2015-09-12 22:22:18 -05:00
|
|
|
// TODO set up tracking events
|
2015-09-11 20:20:49 -05:00
|
|
|
mouseEvent(mouseMoved)
|
|
|
|
mouseEvent(mouseDragged)
|
|
|
|
mouseEvent(rightMouseDragged)
|
|
|
|
mouseEvent(otherMouseDragged)
|
|
|
|
mouseEvent(mouseDown)
|
|
|
|
mouseEvent(rightMouseDown)
|
|
|
|
mouseEvent(otherMouseDown)
|
|
|
|
mouseEvent(mouseUp)
|
|
|
|
mouseEvent(rightMouseUp)
|
|
|
|
mouseEvent(otherMouseUp)
|
|
|
|
|
2015-09-12 07:11:10 -05:00
|
|
|
// note: there is no equivalent to WM_CAPTURECHANGED on Mac OS X; there literally is no way to break a grab like that
|
|
|
|
// even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons
|
|
|
|
// therefore, no DragBroken()
|
|
|
|
|
2015-09-13 18:26:40 -05:00
|
|
|
- (int)sendKeyEvent:(uiAreaKeyEvent *)ke
|
|
|
|
{
|
|
|
|
return (*(self->libui_a->ah->KeyEvent))(self->libui_a->ah, self->libui_a, ke);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)doKeyDownUp:(NSEvent *)e up:(int)up
|
|
|
|
{
|
2015-09-13 20:12:48 -05:00
|
|
|
uiAreaKeyEvent ke;
|
2015-09-13 18:26:40 -05:00
|
|
|
|
|
|
|
ke.Key = 0;
|
|
|
|
ke.ExtKey = 0;
|
|
|
|
ke.Modifier = 0;
|
|
|
|
|
|
|
|
ke.Modifiers = [self parseModifiers:e];
|
|
|
|
|
|
|
|
ke.Up = up;
|
|
|
|
|
|
|
|
if (!fromKeycode([e keyCode], &ke))
|
|
|
|
return 0;
|
|
|
|
return [self sendKeyEvent:&ke];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)doKeyDown:(NSEvent *)e
|
|
|
|
{
|
|
|
|
return [self doKeyDownUp:e up:0];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)doKeyUp:(NSEvent *)e
|
|
|
|
{
|
|
|
|
return [self doKeyDownUp:e up:1];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)doFlagsChanged:(NSEvent *)e
|
|
|
|
{
|
|
|
|
uiAreaKeyEvent ke;
|
|
|
|
uiModifiers whichmod;
|
|
|
|
|
|
|
|
ke.Key = 0;
|
|
|
|
ke.ExtKey = 0;
|
|
|
|
|
|
|
|
// Mac OS X sends this event on both key up and key down.
|
|
|
|
// Fortunately -[e keyCode] IS valid here, so we can simply map from key code to Modifiers, get the value of [e modifierFlags], and check if the respective bit is set or not — that will give us the up/down state
|
|
|
|
if (!keycodeModifier([e keyCode], &whichmod))
|
|
|
|
return 0;
|
|
|
|
ke.Modifier = whichmod;
|
|
|
|
ke.Modifiers = [self parseModifiers:e];
|
|
|
|
ke.Up = (ke.Modifiers & ke.Modifier) == 0;
|
|
|
|
// and then drop the current modifier from Modifiers
|
|
|
|
ke.Modifiers &= ~ke.Modifier;
|
|
|
|
return [self sendKeyEvent:&ke];
|
|
|
|
}
|
|
|
|
|
2015-09-10 13:27:45 -05:00
|
|
|
@end
|
|
|
|
|
2015-09-13 18:26:40 -05:00
|
|
|
// called by subclasses of -[NSApplication sendEvent:]
|
|
|
|
// by default, NSApplication eats some key events
|
|
|
|
// this prevents that from happening with uiArea
|
|
|
|
// see http://stackoverflow.com/questions/24099063/how-do-i-detect-keyup-in-my-nsview-with-the-command-key-held and http://lists.apple.com/archives/cocoa-dev/2003/Oct/msg00442.html
|
|
|
|
int sendAreaEvents(NSEvent *e)
|
|
|
|
{
|
|
|
|
NSEventType type;
|
|
|
|
id focused;
|
|
|
|
areaDrawingView *view;
|
|
|
|
|
|
|
|
type = [e type];
|
|
|
|
if (type != NSKeyDown && type != NSKeyUp && type != NSFlagsChanged)
|
|
|
|
return 0;
|
|
|
|
focused = [[e window] firstResponder];
|
|
|
|
if (focused == nil)
|
|
|
|
return 0;
|
|
|
|
if (![focused isKindOfClass:[areaDrawingView class]])
|
|
|
|
return 0;
|
|
|
|
view = (areaDrawingView *) focused;
|
|
|
|
switch (type) {
|
|
|
|
case NSKeyDown:
|
|
|
|
return [view doKeyDown:e];
|
|
|
|
case NSKeyUp:
|
|
|
|
return [view doKeyUp:e];
|
|
|
|
case NSFlagsChanged:
|
|
|
|
return [view doFlagsChanged:e];
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-10 13:27:45 -05:00
|
|
|
@implementation areaView
|
|
|
|
|
|
|
|
- (id)initWithFrame:(NSRect)r area:(uiArea *)a
|
|
|
|
{
|
|
|
|
NSScrollerStyle style;
|
|
|
|
CGFloat swidth;
|
|
|
|
NSMutableDictionary *views;
|
|
|
|
NSLayoutConstraint *constraint;
|
|
|
|
|
|
|
|
self = [super initWithFrame:r];
|
|
|
|
if (self) {
|
|
|
|
self->libui_a = a;
|
|
|
|
|
|
|
|
self->drawingView = [[areaDrawingView alloc] initWithFrame:NSZeroRect area:self->libui_a];
|
|
|
|
[self->drawingView setTranslatesAutoresizingMaskIntoConstraints:NO];
|
|
|
|
|
|
|
|
style = [NSScroller preferredScrollerStyle];
|
|
|
|
swidth = [NSScroller scrollerWidthForControlSize:NSRegularControlSize
|
|
|
|
scrollerStyle:style];
|
|
|
|
|
|
|
|
self->hscrollbar = [[areaScroller alloc]
|
|
|
|
initWithFrame:NSMakeRect(0, 0, swidth * 5, swidth)
|
|
|
|
vertical:NO];
|
|
|
|
[self->hscrollbar setScrollerStyle:style];
|
|
|
|
[self->hscrollbar setKnobStyle:NSScrollerKnobStyleDefault];
|
|
|
|
[self->hscrollbar setControlTint:NSDefaultControlTint];
|
|
|
|
[self->hscrollbar setControlSize:NSRegularControlSize];
|
2015-09-10 17:31:21 -05:00
|
|
|
[self->hscrollbar setArrowsPosition:NSScrollerArrowsDefaultSetting];
|
2015-09-10 13:27:45 -05:00
|
|
|
[self->hscrollbar setTranslatesAutoresizingMaskIntoConstraints:NO];
|
|
|
|
|
|
|
|
self->vscrollbar = [[areaScroller alloc]
|
|
|
|
initWithFrame:NSMakeRect(0, 0, swidth, swidth * 5)
|
|
|
|
vertical:YES];
|
|
|
|
[self->vscrollbar setScrollerStyle:style];
|
|
|
|
[self->vscrollbar setKnobStyle:NSScrollerKnobStyleDefault];
|
|
|
|
[self->vscrollbar setControlTint:NSDefaultControlTint];
|
|
|
|
[self->vscrollbar setControlSize:NSRegularControlSize];
|
2015-09-10 17:31:21 -05:00
|
|
|
[self->vscrollbar setArrowsPosition:NSScrollerArrowsDefaultSetting];
|
2015-09-10 13:27:45 -05:00
|
|
|
[self->vscrollbar setTranslatesAutoresizingMaskIntoConstraints:NO];
|
|
|
|
|
|
|
|
[self addSubview:self->drawingView];
|
|
|
|
[self addSubview:self->hscrollbar];
|
|
|
|
[self addSubview:self->vscrollbar];
|
|
|
|
|
|
|
|
// use visual constraints to arrange:
|
|
|
|
// - the drawing view and vertical scrollbar horizontally
|
|
|
|
// - the drawing view and horizontal scrollbar vertically
|
|
|
|
// - the horizontal scrollbar flush left
|
|
|
|
// - the vertical scrollbar flush top
|
|
|
|
views = [NSMutableDictionary new];
|
|
|
|
[views setObject:self->drawingView forKey:@"drawingView"];
|
|
|
|
[views setObject:self->hscrollbar forKey:@"hscrollbar"];
|
|
|
|
[views setObject:self->vscrollbar forKey:@"vscrollbar"];
|
|
|
|
addConstraint(self, @"H:|[drawingView][vscrollbar]|", nil, views);
|
|
|
|
addConstraint(self, @"V:|[drawingView][hscrollbar]|", nil, views);
|
|
|
|
addConstraint(self, @"H:|[hscrollbar]", nil, views);
|
|
|
|
addConstraint(self, @"V:|[vscrollbar]", nil, views);
|
|
|
|
[views release];
|
|
|
|
|
|
|
|
// use explicit layout constraints to line up
|
|
|
|
// - the bottom edge of the drawing view with the bottom edge of the vertical scrollbar
|
|
|
|
// - the right edge of the drawing view with the right edge of the horizontal scrollbar
|
|
|
|
constraint = [NSLayoutConstraint constraintWithItem:self->drawingView
|
|
|
|
attribute:NSLayoutAttributeBottom
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:self->vscrollbar
|
|
|
|
attribute:NSLayoutAttributeBottom
|
|
|
|
multiplier:1
|
|
|
|
constant:0];
|
|
|
|
[self addConstraint:constraint];
|
|
|
|
[constraint release];
|
|
|
|
constraint = [NSLayoutConstraint constraintWithItem:self->drawingView
|
|
|
|
attribute:NSLayoutAttributeRight
|
|
|
|
relatedBy:NSLayoutRelationEqual
|
|
|
|
toItem:self->hscrollbar
|
|
|
|
attribute:NSLayoutAttributeRight
|
|
|
|
multiplier:1
|
|
|
|
constant:0];
|
|
|
|
[self addConstraint:constraint];
|
|
|
|
[constraint release];
|
2015-09-10 16:56:09 -05:00
|
|
|
|
|
|
|
self->hscrollpos = 0;
|
|
|
|
self->vscrollpos = 0;
|
|
|
|
|
|
|
|
// now set up events
|
|
|
|
// first we need to monitor when the drawing view frame size has changed, as we need to recalculate all the scrollbar parameters in that case
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
addObserver:self
|
|
|
|
selector:@selector(dvFrameSizeChanged:)
|
|
|
|
name:NSViewFrameDidChangeNotification
|
|
|
|
object:self->drawingView];
|
|
|
|
// and this will trigger a frame changed event to kick us off
|
|
|
|
[self->drawingView setPostsFrameChangedNotifications:YES];
|
|
|
|
|
|
|
|
// and the scrollbar events
|
|
|
|
[self->hscrollbar setTarget:self];
|
|
|
|
[self->hscrollbar setAction:@selector(hscrollEvent:)];
|
|
|
|
[self->vscrollbar setTarget:self];
|
|
|
|
[self->vscrollbar setAction:@selector(vscrollEvent:)];
|
|
|
|
|
|
|
|
// TODO notification on preferred style change
|
2015-09-10 13:27:45 -05:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2015-09-10 16:56:09 -05:00
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[self->vscrollbar setTarget:nil];
|
|
|
|
[self->hscrollbar setTarget:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2015-09-10 17:31:21 -05:00
|
|
|
// TODO reduce code duplication
|
|
|
|
|
|
|
|
// TODO if the proportion becomes 1 we should disable the scrollbar
|
2015-09-10 16:56:09 -05:00
|
|
|
- (void)dvFrameSizeChanged:(NSNotification *)note
|
|
|
|
{
|
|
|
|
intmax_t max;
|
|
|
|
double proportion;
|
|
|
|
|
|
|
|
max = [self hscrollmax];
|
|
|
|
if (max == 0) {
|
|
|
|
[self->hscrollbar setKnobProportion:0];
|
|
|
|
// this hides the knob
|
|
|
|
[self->hscrollbar setEnabled:NO];
|
|
|
|
} else {
|
|
|
|
proportion = [self hpagesize];
|
|
|
|
proportion /= max;
|
|
|
|
[self->hscrollbar setKnobProportion:proportion];
|
|
|
|
[self->hscrollbar setEnabled:YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
max = [self vscrollmax];
|
|
|
|
if (max == 0) {
|
|
|
|
[self->vscrollbar setKnobProportion:0];
|
|
|
|
// this hides the knob
|
|
|
|
[self->vscrollbar setEnabled:NO];
|
|
|
|
} else {
|
|
|
|
proportion = [self vpagesize];
|
|
|
|
proportion /= max;
|
|
|
|
[self->vscrollbar setKnobProportion:proportion];
|
|
|
|
[self->vscrollbar setEnabled:YES];
|
|
|
|
}
|
|
|
|
|
|
|
|
// and update the scrolling position
|
|
|
|
[self hscrollTo:self->hscrollpos];
|
|
|
|
[self vscrollTo:self->vscrollpos];
|
2015-09-10 20:17:00 -05:00
|
|
|
|
2015-12-04 20:04:51 -06:00
|
|
|
// we must redraw everything on resize because Windows requires it
|
|
|
|
[self->drawingView setNeedsDisplay:YES];
|
2015-09-10 16:56:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
- (IBAction)hscrollEvent:(id)sender
|
|
|
|
{
|
|
|
|
uintmax_t pos;
|
|
|
|
|
|
|
|
pos = self->hscrollpos;
|
|
|
|
switch ([self->hscrollbar hitPart]) {
|
|
|
|
case NSScrollerNoPart:
|
|
|
|
// do nothing
|
|
|
|
break;
|
|
|
|
case NSScrollerDecrementPage:
|
|
|
|
pos -= [self hpagesize];
|
|
|
|
break;
|
|
|
|
case NSScrollerKnob:
|
|
|
|
case NSScrollerKnobSlot:
|
|
|
|
pos = [self hscrollbarPosition];
|
|
|
|
break;
|
|
|
|
case NSScrollerIncrementPage:
|
|
|
|
pos += [self hpagesize];
|
|
|
|
break;
|
|
|
|
case NSScrollerDecrementLine:
|
|
|
|
pos--;
|
|
|
|
break;
|
|
|
|
case NSScrollerIncrementLine:
|
|
|
|
pos++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
[self hscrollTo:pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (IBAction)vscrollEvent:(id)sender
|
|
|
|
{
|
|
|
|
uintmax_t pos;
|
|
|
|
|
|
|
|
pos = self->vscrollpos;
|
|
|
|
switch ([self->vscrollbar hitPart]) {
|
|
|
|
case NSScrollerNoPart:
|
|
|
|
// do nothing
|
|
|
|
break;
|
|
|
|
case NSScrollerDecrementPage:
|
|
|
|
pos -= [self vpagesize];
|
|
|
|
break;
|
|
|
|
case NSScrollerKnob:
|
|
|
|
case NSScrollerKnobSlot:
|
|
|
|
pos = [self vscrollbarPosition];
|
|
|
|
break;
|
|
|
|
case NSScrollerIncrementPage:
|
|
|
|
pos += [self vpagesize];
|
|
|
|
break;
|
|
|
|
case NSScrollerDecrementLine:
|
|
|
|
pos--;
|
|
|
|
break;
|
|
|
|
case NSScrollerIncrementLine:
|
|
|
|
pos++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
[self vscrollTo:pos];
|
|
|
|
}
|
|
|
|
|
2015-09-11 20:20:49 -05:00
|
|
|
- (intmax_t)hscrollPos
|
|
|
|
{
|
|
|
|
return self->hscrollpos;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (intmax_t)vscrollPos
|
|
|
|
{
|
|
|
|
return self->vscrollpos;
|
|
|
|
}
|
|
|
|
|
2015-09-10 16:56:09 -05:00
|
|
|
// scroll utilities
|
|
|
|
|
|
|
|
- (intmax_t)hpagesize
|
|
|
|
{
|
|
|
|
return [self->drawingView frame].size.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (intmax_t)vpagesize
|
|
|
|
{
|
|
|
|
return [self->drawingView frame].size.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (intmax_t)hscrollmax
|
|
|
|
{
|
|
|
|
intmax_t n;
|
|
|
|
|
|
|
|
n = (*(self->libui_a->ah->HScrollMax))(self->libui_a->ah, self->libui_a);
|
|
|
|
n -= [self hpagesize];
|
|
|
|
if (n < 0)
|
|
|
|
n = 0;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (intmax_t)vscrollmax
|
|
|
|
{
|
|
|
|
intmax_t n;
|
|
|
|
|
|
|
|
n = (*(self->libui_a->ah->VScrollMax))(self->libui_a->ah, self->libui_a);
|
|
|
|
n -= [self vpagesize];
|
|
|
|
if (n < 0)
|
|
|
|
n = 0;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (intmax_t)hscrollbarPosition
|
|
|
|
{
|
|
|
|
return [self->hscrollbar doubleValue] * [self hscrollmax];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (intmax_t)vscrollbarPosition
|
|
|
|
{
|
|
|
|
return [self->vscrollbar doubleValue] * [self vscrollmax];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)hscrollTo:(intmax_t)pos
|
|
|
|
{
|
|
|
|
double doubleVal;
|
2015-09-10 17:31:21 -05:00
|
|
|
CGFloat by;
|
|
|
|
NSRect update;
|
2015-09-10 16:56:09 -05:00
|
|
|
|
|
|
|
if (pos > [self hscrollmax])
|
|
|
|
pos = [self hscrollmax];
|
|
|
|
if (pos < 0)
|
|
|
|
pos = 0;
|
|
|
|
|
2015-09-10 17:31:21 -05:00
|
|
|
by = -(pos - self->hscrollpos);
|
|
|
|
[self->drawingView scrollRect:[self->drawingView bounds]
|
|
|
|
by:NSMakeSize(by, 0)];
|
|
|
|
update = [self->drawingView bounds];
|
|
|
|
if (by < 0) { // right of bounds needs updating
|
|
|
|
// + by since by is negative and we need to subtract its absolute value from the width
|
|
|
|
update.origin.x += update.size.width + by;
|
|
|
|
update.size.width = -by;
|
|
|
|
} else // left of bounds needs updating
|
|
|
|
update.size.width = by;
|
|
|
|
[self->drawingView setNeedsDisplayInRect:update];
|
2015-09-10 16:56:09 -05:00
|
|
|
|
|
|
|
self->hscrollpos = pos;
|
|
|
|
doubleVal = ((double) (self->hscrollpos)) / [self hscrollmax];
|
|
|
|
[self->hscrollbar setDoubleValue:doubleVal];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)vscrollTo:(intmax_t)pos
|
|
|
|
{
|
|
|
|
double doubleVal;
|
2015-09-10 17:31:21 -05:00
|
|
|
CGFloat by;
|
|
|
|
NSRect update;
|
2015-09-10 16:56:09 -05:00
|
|
|
|
|
|
|
if (pos > [self vscrollmax])
|
|
|
|
pos = [self vscrollmax];
|
|
|
|
if (pos < 0)
|
|
|
|
pos = 0;
|
|
|
|
|
2015-09-10 17:31:21 -05:00
|
|
|
by = -(pos - self->vscrollpos);
|
|
|
|
[self->drawingView scrollRect:[self->drawingView bounds]
|
|
|
|
by:NSMakeSize(0, by)];
|
|
|
|
update = [self->drawingView bounds];
|
|
|
|
if (by < 0) { // bottom of bounds needs updating
|
|
|
|
// + by since by is negative and we need to subtract its absolute value from the height
|
|
|
|
update.origin.y += update.size.height + by;
|
|
|
|
update.size.height = -by;
|
|
|
|
} else // top of bounds needs updating
|
|
|
|
update.size.height = by;
|
|
|
|
[self->drawingView setNeedsDisplayInRect:update];
|
2015-09-10 16:56:09 -05:00
|
|
|
|
|
|
|
self->vscrollpos = pos;
|
|
|
|
doubleVal = ((double) (self->vscrollpos)) / [self vscrollmax];
|
|
|
|
[self->vscrollbar setDoubleValue:doubleVal];
|
|
|
|
}
|
|
|
|
|
2015-09-10 13:27:45 -05:00
|
|
|
@end
|
|
|
|
|
2015-10-09 11:33:45 -05:00
|
|
|
void uiAreaUpdateScroll(uiArea *a)
|
|
|
|
{
|
|
|
|
/* TODO
|
|
|
|
NSRect frame;
|
|
|
|
|
|
|
|
frame.origin = NSMakePoint(0, 0);
|
|
|
|
frame.size.width = (*(a->ah->HScrollMax))(a->ah, a);
|
|
|
|
frame.size.height = (*(a->ah->VScrollMax))(a->ah, a);
|
|
|
|
[a->documentView setFrame:frame];
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2015-10-09 14:27:57 -05:00
|
|
|
void uiAreaQueueRedrawAll(uiArea *a)
|
|
|
|
{
|
|
|
|
[a->view setNeedsDisplay:YES];
|
|
|
|
}
|
|
|
|
|
2015-10-09 11:33:45 -05:00
|
|
|
uiArea *uiNewArea(uiAreaHandler *ah)
|
2015-09-10 13:27:45 -05:00
|
|
|
{
|
|
|
|
uiArea *a;
|
|
|
|
|
2015-10-09 11:33:45 -05:00
|
|
|
a = (uiArea *) uiNewControl(uiAreaType());
|
2015-09-10 13:27:45 -05:00
|
|
|
|
|
|
|
a->ah = ah;
|
|
|
|
|
|
|
|
a->view = [[areaView alloc] initWithFrame:NSZeroRect area:a];
|
|
|
|
|
2015-10-09 11:33:45 -05:00
|
|
|
uiDarwinFinishNewControl(a, uiArea);
|
2015-09-10 13:27:45 -05:00
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|