252 lines
6.7 KiB
Text
252 lines
6.7 KiB
Text
gui.widgets.ScrollArea = {
|
|
new: func(parent, style, cfg)
|
|
{
|
|
var cfg = Config.new(cfg);
|
|
var m = gui.Widget.new(gui.widgets.ScrollArea);
|
|
m._focus_policy = m.NoFocus;
|
|
m._content_pos = [0, 0];
|
|
m._scroller_pos = [0, 0];
|
|
m._max_scroll = [0, 0];
|
|
m._layout = nil;
|
|
|
|
if( style != nil )
|
|
m._setView( style.createWidget(parent, "scroll-area", cfg) );
|
|
|
|
m.setMinimumSize([32, 32]);
|
|
|
|
return m;
|
|
},
|
|
setLayout: func(l)
|
|
{
|
|
me._layout = l;
|
|
l.setParent(me);
|
|
return me.update();
|
|
},
|
|
getContent: func()
|
|
{
|
|
return me._view.content;
|
|
},
|
|
# Set the background color for the content area.
|
|
#
|
|
# @param color Vector of 3 or 4 values in [0, 1]
|
|
setColorBackground: func
|
|
{
|
|
if( size(arg) == 1 )
|
|
var arg = arg[0];
|
|
me._view.setColorBackground(arg);
|
|
return me;
|
|
},
|
|
# Reset the size of the content area, e.g. on window resize.
|
|
#
|
|
# @param sz Vector of [x,y] values.
|
|
setSize: func
|
|
{
|
|
if( size(arg) == 1 )
|
|
var arg = arg[0];
|
|
var (x,y) = arg;
|
|
me._size = [x,y];
|
|
return me.update();
|
|
},
|
|
# Move contents to the coordinates x,y (or as far as possible)
|
|
#
|
|
# @param x The x coordinate (positive is right)
|
|
# @param y The y coordinate (positive is down)
|
|
scrollTo: func(x, y)
|
|
{
|
|
me._content_pos[0] = x;
|
|
me._content_pos[1] = y;
|
|
|
|
return me.update();
|
|
},
|
|
# Move the scrollable area to the top-most position
|
|
scrollToTop: func me.scrollTo( me._content_pos[0], 0 ),
|
|
# Move the scrollable area to the bottom-most position
|
|
scrollToBottom: func me.scrollTo( me._content_pos[0], me._max_scroll[1] ),
|
|
# Move the scrollable area to the left-most position
|
|
scrollToLeft: func me.scrollTo( 0, me._content_pos[1] ),
|
|
# Move the scrollable area to the right-most position
|
|
scrollToRight: func me.scrollTo( me._max_scroll[0], me._content_pos[1] ),
|
|
# Move content by given delta
|
|
scrollBy: func(x, y)
|
|
{
|
|
return me.scrollTo( me._content_pos[0] + x,
|
|
me._content_pos[1] + y );
|
|
},
|
|
# Set horizontal scrollbar position
|
|
horizScrollBarTo: func(x)
|
|
{
|
|
if( me._scroller_delta[0] < 1 )
|
|
return me;
|
|
|
|
me.scrollTo( me._max_scroll[0] * (x / me._scroller_delta[0]),
|
|
me._content_pos[1] );
|
|
},
|
|
# Set vertical scrollbar position
|
|
vertScrollBarTo: func(y)
|
|
{
|
|
if( me._scroller_delta[1] < 1 )
|
|
return me;
|
|
|
|
me.scrollTo( me._content_pos[0],
|
|
me._max_scroll[1] * (y / me._scroller_delta[1]) );
|
|
},
|
|
# Move horizontal scrollbar by given offset
|
|
horizScrollBarBy: func(dx)
|
|
{
|
|
me.horizScrollBarTo(me._scroller_pos[0] + dx);
|
|
},
|
|
# Move vertical scrollbar by given offset
|
|
vertScrollBarBy: func(dy)
|
|
{
|
|
me.vertScrollBarTo(me._scroller_pos[1] + dy);
|
|
},
|
|
# Update scroll bar and content area.
|
|
#
|
|
# Needs to be called when the size of the content changes.
|
|
update: func(bb=nil)
|
|
{
|
|
if (bb == nil) bb = me._updateBB();
|
|
if (bb == nil) return me;
|
|
|
|
var offset = [ me._content_offset[0] - me._content_pos[0],
|
|
me._content_offset[1] - me._content_pos[1] ];
|
|
me.getContent().setTranslation(offset);
|
|
|
|
me._view.update(me);
|
|
me.getContent().update();
|
|
|
|
return me;
|
|
},
|
|
# protected:
|
|
_setView: func(view)
|
|
{
|
|
call(gui.Widget._setView, [view], me);
|
|
|
|
view.vert.addEventListener("mousedown", func(e) me._dragStart(e));
|
|
view.horiz.addEventListener("mousedown", func(e) me._dragStart(e));
|
|
view._root.addEventListener("mousedown", func(e)
|
|
{
|
|
me._drag_offsetX = me._content_pos[0] + e.clientX;
|
|
me._drag_offsetY = me._content_pos[1] + e.clientY;
|
|
});
|
|
|
|
view.vert.addEventListener
|
|
(
|
|
"drag",
|
|
func(e)
|
|
{
|
|
if( !me._enabled )
|
|
return;
|
|
|
|
me.vertScrollBarTo(me._drag_offsetY + e.clientY);
|
|
e.stopPropagation();
|
|
}
|
|
);
|
|
view.horiz.addEventListener
|
|
(
|
|
"drag",
|
|
func(e)
|
|
{
|
|
if( !me._enabled )
|
|
return;
|
|
|
|
me.horizScrollBarTo(me._drag_offsetX + e.clientX);
|
|
e.stopPropagation();
|
|
}
|
|
);
|
|
|
|
view._root.addEventListener
|
|
(
|
|
"drag",
|
|
func(e)
|
|
{
|
|
if( !me._enabled )
|
|
return;
|
|
|
|
me.scrollTo( me._drag_offsetX - e.clientX,
|
|
me._drag_offsetY - e.clientY );
|
|
e.stopPropagation();
|
|
}
|
|
);
|
|
view._root.addEventListener
|
|
(
|
|
"wheel",
|
|
func(e)
|
|
{
|
|
if( !me._enabled )
|
|
return;
|
|
|
|
me.scrollBy(0, 30 * -e.deltaY); # TODO make step size configurable
|
|
e.stopPropagation();
|
|
}
|
|
);
|
|
},
|
|
_dragStart: func(e)
|
|
{
|
|
me._drag_offsetX = me._scroller_pos[0] - e.clientX;
|
|
me._drag_offsetY = me._scroller_pos[1] - e.clientY;
|
|
e.stopPropagation();
|
|
},
|
|
_updateBB: func()
|
|
{
|
|
# TODO only update on content resize
|
|
if( me._layout == nil )
|
|
{
|
|
var bb = me.getContent().getTightBoundingBox();
|
|
|
|
if( bb[2] < bb[0] or bb[3] < bb[1] )
|
|
return nil;
|
|
var w = bb[2] - bb[0];
|
|
var h = bb[3] - bb[1];
|
|
|
|
var cur_offset = me.getContent().getTranslation();
|
|
me._content_offset = [cur_offset[0] - bb[0], cur_offset[1] - bb[1]];
|
|
}
|
|
else
|
|
{
|
|
var min_size = me._layout.minimumSize();
|
|
var max_size = me._layout.maximumSize();
|
|
var size_hint = me._layout.sizeHint();
|
|
var w = math.min(max_size[0], math.max(size_hint[0], me._size[0]));
|
|
var h = math.max(
|
|
math.min(max_size[1], math.max(size_hint[1], me._size[1])),
|
|
me._layout.heightForWidth(w)
|
|
);
|
|
|
|
me._layout.setGeometry([0, 0, w, h]);
|
|
|
|
# Layout always has the origin at (0, 0)
|
|
me._content_offset = [0, 0];
|
|
}
|
|
|
|
me._max_scroll[0] = math.max(0, w - me._size[0]);
|
|
me._max_scroll[1] = math.max(0, h - me._size[1]);
|
|
me._content_size = [w, h];
|
|
|
|
# keep position within limit and only integer (to prevent artifacts on text,
|
|
# lines, etc. not alligned with pixel grid)
|
|
me._content_pos[0] =
|
|
math.max(0, math.min( math.round(me._content_pos[0]), me._max_scroll[0]));
|
|
me._content_pos[1] =
|
|
math.max(0, math.min( math.round(me._content_pos[1]), me._max_scroll[1]));
|
|
|
|
me._scroller_size = [0, 0]; # scroller size
|
|
me._scroller_offset = [0, 0]; # scroller minimum pos (eg. add offset for
|
|
# scrolling with buttons)
|
|
me._scroller_delta = [0, 0]; # scroller max travel distance
|
|
|
|
# update scroller size/offset/max delta
|
|
me._view._updateScrollMetrics(me, 0);
|
|
me._view._updateScrollMetrics(me, 1);
|
|
|
|
# update current scrollbar positions
|
|
me._scroller_pos[0] =
|
|
me._max_scroll[0] > 0
|
|
? (me._content_pos[0] / me._max_scroll[0]) * me._scroller_delta[0]
|
|
: 0;
|
|
me._scroller_pos[1] =
|
|
me._max_scroll[1] > 0
|
|
? (me._content_pos[1] / me._max_scroll[1]) * me._scroller_delta[1]
|
|
: 0;
|
|
}
|
|
};
|