canvas.gui: Basic ScrollArea widget.
This commit is contained in:
parent
313175b943
commit
d7ddeb7183
6 changed files with 187 additions and 49 deletions
|
@ -8,10 +8,12 @@ var gui_dir = getprop("/sim/fg-root") ~ "/Nasal/canvas/gui/";
|
|||
var loadGUIFile = func(file) io.load_nasal(gui_dir ~ file, "canvas");
|
||||
var loadWidget = func(name) loadGUIFile("widgets/" ~ name ~ ".nas");
|
||||
|
||||
loadGUIFile("Config.nas");
|
||||
loadGUIFile("Style.nas");
|
||||
loadGUIFile("Widget.nas");
|
||||
loadGUIFile("styles/DefaultStyle.nas");
|
||||
loadWidget("Button");
|
||||
loadWidget("ScrollArea");
|
||||
|
||||
var style = DefaultStyle.new("AmbianceClassic");
|
||||
var WindowButton = {
|
||||
|
|
21
Nasal/canvas/gui/Config.nas
Normal file
21
Nasal/canvas/gui/Config.nas
Normal file
|
@ -0,0 +1,21 @@
|
|||
var Config = {
|
||||
new: func(cfg)
|
||||
{
|
||||
var m = {
|
||||
parents: [Config],
|
||||
_cfg: cfg
|
||||
};
|
||||
if( typeof(m._cfg) != "hash" )
|
||||
m._cfg = {};
|
||||
|
||||
return m;
|
||||
},
|
||||
get: func(key, default = nil)
|
||||
{
|
||||
var val = me._cfg[key];
|
||||
if( val != nil )
|
||||
return val;
|
||||
|
||||
return default;
|
||||
}
|
||||
};
|
|
@ -13,7 +13,8 @@ gui.Widget = {
|
|||
_focused: 0,
|
||||
_focus_policy: gui.Widget.NoFocus,
|
||||
_hover: 0,
|
||||
_root: nil
|
||||
_root: nil,
|
||||
_size: [64, 64]
|
||||
};
|
||||
},
|
||||
# Move the widget to the given position (relative to its parent)
|
||||
|
@ -23,6 +24,12 @@ gui.Widget = {
|
|||
return me;
|
||||
},
|
||||
#
|
||||
setSize: func(w, h)
|
||||
{
|
||||
me._size[0] = w;
|
||||
me._size[1] = h;
|
||||
},
|
||||
#
|
||||
setFocus: func
|
||||
{
|
||||
if( me._focused )
|
||||
|
|
|
@ -12,7 +12,12 @@ var DefaultStyle = {
|
|||
return nil;
|
||||
}
|
||||
|
||||
return factory.new(parent, me, cfg);
|
||||
var w = {
|
||||
parents: [factory],
|
||||
_style: me
|
||||
};
|
||||
call(factory.new, [parent, cfg], w);
|
||||
return w;
|
||||
},
|
||||
widgets: {}
|
||||
};
|
||||
|
@ -20,31 +25,26 @@ var DefaultStyle = {
|
|||
# A button
|
||||
DefaultStyle.widgets.button = {
|
||||
padding: [6, 8, 6, 8],
|
||||
new: func(parent, style, cfg)
|
||||
new: func(parent, cfg)
|
||||
{
|
||||
var button = {
|
||||
parents: [DefaultStyle.widgets.button],
|
||||
element: parent.createChild("group", "button"),
|
||||
size: cfg.get("size", [26, 26]),
|
||||
_style: style
|
||||
};
|
||||
me.element = parent.createChild("group", "button");
|
||||
me.size = cfg.get("size", [26, 26]);
|
||||
|
||||
button._bg =
|
||||
button.element.rect( 3,
|
||||
3,
|
||||
button.size[0] - 6,
|
||||
button.size[1] - 6,
|
||||
{"border-radius": 5} );
|
||||
button._border =
|
||||
button.element.createChild("image", "button")
|
||||
.set("slice", "10 12") #"7")
|
||||
.setSize(button.size);
|
||||
button._label =
|
||||
button.element.createChild("text")
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.set("character-size", 14)
|
||||
.set("alignment", "center-baseline");
|
||||
return button;
|
||||
me._bg =
|
||||
me.element.rect( 3,
|
||||
3,
|
||||
me.size[0] - 6,
|
||||
me.size[1] - 6,
|
||||
{"border-radius": 5} );
|
||||
me._border =
|
||||
me.element.createChild("image", "button")
|
||||
.set("slice", "10 12") #"7")
|
||||
.setSize(me.size);
|
||||
me._label =
|
||||
me.element.createChild("text")
|
||||
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.set("character-size", 14)
|
||||
.set("alignment", "center-baseline");
|
||||
},
|
||||
setText: func(text)
|
||||
{
|
||||
|
@ -85,3 +85,46 @@ DefaultStyle.widgets.button = {
|
|||
me._border.set("file", file ~ ".png");
|
||||
}
|
||||
};
|
||||
|
||||
# ScrollArea
|
||||
DefaultStyle.widgets["scroll-area"] = {
|
||||
new: func(parent, cfg)
|
||||
{
|
||||
me.element = parent.createChild("group", "scroll-area");
|
||||
|
||||
me._bg = me.element.createChild("path", "background")
|
||||
.set("fill", "#e0e0e0");
|
||||
me.content = me.element.createChild("group", "scroll-content")
|
||||
.set("clip-frame", Element.PARENT);
|
||||
me.vert = me._newScroll(me.element, "vert");
|
||||
me.horiz = me._newScroll(me.element, "horiz");
|
||||
},
|
||||
update: func(widget)
|
||||
{
|
||||
me.horiz.reset();
|
||||
if( widget._max_scroll[0] > 1 )
|
||||
# only show scroll bar if horizontally scrollable
|
||||
me.horiz.moveTo(widget._pos[0], widget._size[1] - 2)
|
||||
.horiz(widget._size[0] - widget._max_scroll[0]);
|
||||
|
||||
me.vert.reset();
|
||||
if( widget._max_scroll[1] > 1 )
|
||||
# only show scroll bar if vertically scrollable
|
||||
me.vert.moveTo(widget._size[0] - 2, widget._pos[1])
|
||||
.vert(widget._size[1] - widget._max_scroll[1]);
|
||||
|
||||
me._bg.reset()
|
||||
.rect(0, 0, widget._size[0], widget._size[1]);
|
||||
me.content.set(
|
||||
"clip",
|
||||
"rect(0, " ~ widget._size[0] ~ ", " ~ widget._size[1] ~ ", 0)"
|
||||
);
|
||||
},
|
||||
# private:
|
||||
_newScroll: func(el, orient)
|
||||
{
|
||||
return el.createChild("path", "scroll-" ~ orient)
|
||||
.set("stroke", "#f07845")
|
||||
.set("stroke-width", 4);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,25 +1,3 @@
|
|||
var Config = {
|
||||
new: func(cfg)
|
||||
{
|
||||
var m = {
|
||||
parents: [Config],
|
||||
_cfg: cfg
|
||||
};
|
||||
if( typeof(m._cfg) != "hash" )
|
||||
m._cfg = {};
|
||||
|
||||
return m;
|
||||
},
|
||||
get: func(key, default = nil)
|
||||
{
|
||||
var val = me._cfg[key];
|
||||
if( val != nil )
|
||||
return val;
|
||||
|
||||
return default;
|
||||
}
|
||||
};
|
||||
|
||||
gui.widgets.Button = {
|
||||
new: func(parent, style, cfg)
|
||||
{
|
||||
|
@ -82,5 +60,3 @@ gui.widgets.Button = {
|
|||
call(gui.Widget._setRoot, [el], me);
|
||||
}
|
||||
};
|
||||
|
||||
return;
|
||||
|
|
89
Nasal/canvas/gui/widgets/ScrollArea.nas
Normal file
89
Nasal/canvas/gui/widgets/ScrollArea.nas
Normal file
|
@ -0,0 +1,89 @@
|
|||
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.StrongFocus;
|
||||
m._active = 0;
|
||||
m._pos = [0,0];
|
||||
m._size = cfg.get("size", m._size);
|
||||
|
||||
if( style != nil )
|
||||
{
|
||||
m._scroll = style.createWidget(parent, "scroll-area", cfg);
|
||||
m._setRoot(m._scroll.element);
|
||||
|
||||
m._scroll.vert.addEventListener("mousedown", func(e) m._dragStart(e));
|
||||
m._scroll.horiz.addEventListener("mousedown", func(e) m._dragStart(e));
|
||||
|
||||
m._scroll.vert.addEventListener
|
||||
(
|
||||
"drag",
|
||||
func(e) m.moveTo(m._pos[0], m._drag_offsetY + e.clientY)
|
||||
);
|
||||
m._scroll.horiz.addEventListener
|
||||
(
|
||||
"drag",
|
||||
func(e) m.moveTo(m._drag_offsetX + e.clientX, m._pos[1])
|
||||
);
|
||||
}
|
||||
|
||||
return m;
|
||||
},
|
||||
getContent: func()
|
||||
{
|
||||
return me._scroll.content;
|
||||
},
|
||||
moveTo: func(x, y)
|
||||
{
|
||||
me._pos[0] = math.max(0, math.min(x, me._max_scroll[0]));
|
||||
me._pos[1] = math.max(0, math.min(y, me._max_scroll[1]));
|
||||
|
||||
me.update();
|
||||
},
|
||||
update: func()
|
||||
{
|
||||
# TODO only update on content resize
|
||||
var bb = me.getContent().getTransformedBounds();
|
||||
var w = bb[2] - bb[0];
|
||||
var h = bb[3] - bb[1];
|
||||
|
||||
me._max_scroll = [0, 0];
|
||||
if( w > me._size[0] )
|
||||
me._max_scroll[0] = me._size[0] * (1 - me._size[0] / w);
|
||||
if( h > me._size[1] )
|
||||
me._max_scroll[1] = me._size[1] * (1 - me._size[1] / h);
|
||||
|
||||
me._content_size = [w, h];
|
||||
|
||||
var cur_offset = me.getContent().getTranslation();
|
||||
me._content_offset = [cur_offset[0] - bb[0], cur_offset[1] - bb[1]];
|
||||
|
||||
var offset = [ me._content_offset[0],
|
||||
me._content_offset[1] ];
|
||||
|
||||
if( me._max_scroll[0] > 1 )
|
||||
offset[0] -= (me._pos[0] / me._max_scroll[0])
|
||||
* (me._content_size[0] - me._size[0]);
|
||||
if( me._max_scroll[1] > 1 )
|
||||
offset[1] -= (me._pos[1] / me._max_scroll[1])
|
||||
* (me._content_size[1] - me._size[1]);
|
||||
|
||||
me.getContent().setTranslation(offset);
|
||||
|
||||
me._scroll.update(me);
|
||||
me.getContent().update();
|
||||
},
|
||||
# protected:
|
||||
_setRoot: func(el)
|
||||
{
|
||||
el.addEventListener("wheel", func(e) me.moveTo(me._pos[0], me._pos[1] - e.deltaY));
|
||||
|
||||
call(gui.Widget._setRoot, [el], me);
|
||||
},
|
||||
_dragStart: func(e)
|
||||
{
|
||||
me._drag_offsetX = me._pos[0] - e.clientX;
|
||||
me._drag_offsetY = me._pos[1] - e.clientY;
|
||||
}
|
||||
};
|
Loading…
Add table
Reference in a new issue