quakeforge/ruamoko/qwaq/ui/scrollbar.r
Bill Currie 2572011a4b [qwaq] Fix various scrollbar related issues
The editor now uses the vertical scrollbar for handling mouse wheel
scrolling, thus keeping the scrollbar in sync.

Scrollbar index can now cover the full range (not sure why I had that
-1), and the potential divide by zero is avoided properly.

Thumb-tab now positioned properly when the range is 0.
2021-06-12 01:30:44 +09:00

261 lines
5 KiB
R

#include "ruamoko/qwaq/ui/button.h"
#include "ruamoko/qwaq/ui/group.h"
#include "ruamoko/qwaq/ui/listener.h"
#include "ruamoko/qwaq/ui/scrollbar.h"
@implementation ScrollBar
-initWithRect:(Rect)rect
{
if (!(self = [super initWithRect:rect])) {
return nil;
}
options = ofRelativeEvents;
buffer = [[DrawBuffer buffer:size] retain];
objects = [[Group withContext:buffer owner:self] retain];
onScrollBarModified = [[ListenerGroup listener] retain];
vertical = xlen == 1;
DrawBuffer *icons[3] = {
[DrawBuffer buffer:{1, 1}],
[DrawBuffer buffer:{1, 1}],
[DrawBuffer buffer:{1, 1}],
};
[icons[2] addch:acs_char (ACS_DIAMOND)];
Point thumbPos;
SEL thumbSel;
growMode = gfGrowAll;
if (vertical) {
[icons[0] addch:acs_char (ACS_UARROW)];
[icons[1] addch:acs_char (ACS_DARROW)];
thumbPos = {0, 1};
thumbSel = @selector(verticalSlide:);
growMode &= ~gfGrowLoY;
} else {
[icons[0] addch:acs_char (ACS_LARROW)];
[icons[1] addch:acs_char (ACS_RARROW)];
thumbPos = {1, 0};
thumbSel = @selector(horizontalSlide:);
growMode &= ~gfGrowLoX;
}
bgchar = acs_char (ACS_CKBOARD);
backButton = [Button withPos:{0, 0}
releasedIcon:icons[0] pressedIcon:icons[0]];
forwardButton = [Button withPos:{xlen - 1, ylen - 1}
releasedIcon:icons[1] pressedIcon:icons[1]];
thumbTab = [Button withPos:thumbPos
releasedIcon:icons[2] pressedIcon:icons[2]];
[forwardButton setGrowMode:gfGrowAll];
[[backButton onClick] addListener:self :@selector(scrollBack:)];
[[forwardButton onClick] addListener:self :@selector(scrollForward:)];
[[thumbTab onPress] addListener:self :thumbSel];
singleStep = 1;
[objects insert:backButton];
[objects insert:forwardButton];
[objects insert:thumbTab];
return self;
}
-(void)dealloc
{
[objects release];
[buffer release];
[onScrollBarModified release];
[super dealloc];
}
+(ScrollBar *)horizontal:(unsigned)len at:(Point)pos
{
if (len == 1) {
[self error:"can't make scrollbar of length 1"];
}
return [[[self alloc] initWithRect:{pos, {len, 1}}] autorelease];
}
+(ScrollBar *)vertical:(unsigned)len at:(Point)pos
{
if (len == 1) {
[self error:"can't make scrollbar of length 1"];
}
return [[[self alloc] initWithRect:{pos, {1, len}}] autorelease];
}
-(ListenerGroup *)onScrollBarModified
{
return onScrollBarModified;
}
-updateAbsPos: (Point) absPos
{
[super updateAbsPos: absPos];
[objects updateAbsPos: absRect.offset];
return self;
}
-draw
{
[super draw];
if (vertical) {
[buffer mvvline:{0,0}, bgchar, ylen];
} else {
[buffer mvhline:{0,0}, bgchar, xlen];
}
[objects draw];
[textContext blitFromBuffer:buffer to:pos from:[buffer rect]];
return self;
}
static void
position_tab (ScrollBar *self)
{
Point p = {0, 0};
if (self.range > 0) {
if (self.vertical) {
p.y = 1 + self.index * (self.ylen - 3) / self.range;
} else {
p.x = 1 + self.index * (self.xlen - 3) / self.range;
}
} else {
if (self.vertical) {
p.y = 1;
} else {
p.x = 1;
}
}
[self.thumbTab moveTo:p];
[self redraw];
}
-resize:(Extent)delta
{
Extent size = self.size;
[super resize:delta];
delta = {self.size.width - size.width, self.size.height - size.height};
[objects resize:delta];
[buffer resizeTo:self.size];
position_tab (self);
return self;
}
-page:(unsigned)step dir:(unsigned) dir
{
unsigned oind = index;
if (dir) {
if (range - index < step) {
step = range - index;
}
index += step;
} else {
if (index < step) {
step = index;
}
index -= step;
}
if (index != oind) {
[onScrollBarModified respond:self];
position_tab (self);
}
return self;
}
static void
page (ScrollBar *self, Point pos, Point thumb)
{
unsigned pageDir = 0;
if (self.vertical) {
if (pos.y < thumb.y) {
pageDir = 0;
} else {
pageDir = 1;
}
} else {
if (pos.x < thumb.x) {
pageDir = 0;
} else {
pageDir = 1;
}
}
[self page:self.pageStep dir:pageDir];
}
-(void)scrollBack:(id)sender
{
[self page:singleStep dir:0];
}
-(void)scrollForward:(id)sender
{
[self page:singleStep dir:1];
}
-(void)horizontalSlide:(id)sender
{
}
-(void)verticalSlide:(id)sender
{
}
-handleEvent:(qwaq_event_t *)event
{
[super handleEvent: event];
[objects handleEvent: event];
if (event.what == qe_mousedown) {
[self grabMouse];
mouseTime = event.when;
mouseStart = {event.mouse.x, event.mouse.y};
tabStart = [thumbTab origin];
page(self, mouseStart, tabStart);
event.what = qe_none;
} else if (event.what==qe_mouseauto) {
if (event.when - mouseTime > 0.1) {
mouseTime = event.when;
page(self, mouseStart, tabStart);
}
event.what = qe_none;
} else if (event.what == qe_mouseup) {
[self releaseMouse];
event.what = qe_none;
}
return self;
}
-setRange:(unsigned)range
{
self.range = range;
return self;
}
-setPageStep:(unsigned)pageStep
{
self.pageStep = pageStep;
return self;
}
-setSingleStep:(unsigned)singleStep
{
self.singleStep = singleStep;
return self;
}
-setIndex:(unsigned)index
{
if (index > self.index) {
[self page:index - self.index dir:1];
} else {
[self page:self.index - index dir:0];
}
return self;
}
-(unsigned)index
{
return index;
}
@end