1
0
Fork 0
fgdata/Nasal/canvas/gui/styles/DefaultStyle.nas
Thomas Geymayer 0338d82c55 canvas.gui.ScrollArea: fix (wheel) scroll behavior.
Rewrite the way scrolling for ScrollAreas is handled: Store
content position instead of scrollbar positions to keep position
on resize and promote moving the content instead of the contents
to as primary API.
Let the mousewheel scroll by fixed content offset instead of
scrollbar offset to make it actually usable (especially with
low scrolling distance).
2014-06-26 00:50:46 +02:00

363 lines
8.8 KiB
Text

var DefaultStyle = {
new: func(name, name_icon_theme)
{
return {
parents: [ gui.Style.new(name, name_icon_theme),
DefaultStyle ]
};
},
createWidget: func(parent, type, cfg)
{
var factory = me.widgets[type];
if( factory == nil )
{
debug.warn("DefaultStyle: unknown widget type (" ~ type ~ ")");
return nil;
}
var w = {
parents: [factory],
_style: me
};
call(factory.new, [parent, cfg], w);
return w;
},
widgets: {}
};
# A button
DefaultStyle.widgets.button = {
new: func(parent, cfg)
{
me._root = parent.createChild("group", "button");
me._bg =
me._root.createChild("path");
me._border =
me._root.createChild("image", "button")
.set("slice", "10 12"); #"7")
me._label =
me._root.createChild("text")
.set("font", "LiberationFonts/LiberationSans-Regular.ttf")
.set("character-size", 14)
.set("alignment", "center-baseline");
},
setSize: func(model, w, h)
{
me._bg.reset()
.rect(3, 3, w - 6, h - 6, {"border-radius": 5});
me._border.setSize(w, h);
},
setText: func(model, text)
{
me._label.set("text", text);
var min_width = math.max(80, me._label.maxWidth() + 16);
model.setMinimumSize([min_width, 16]);
model.setSizeHint([min_width, 28]);
return me;
},
update: func(model)
{
var backdrop = !model._windowFocus();
var (w, h) = model._size;
var file = me._style._dir_widgets ~ "/";
# TODO unify color names with image names
var bg_color_name = "button_bg_color";
if( backdrop )
bg_color_name = "button_backdrop_bg_color";
else if( !model._enabled )
bg_color_name = "button_bg_color_insensitive";
else if( model._down )
bg_color_name = "button_bg_color_down";
else if( model._hover )
bg_color_name = "button_bg_color_hover";
me._bg.set("fill", me._style.getColor(bg_color_name));
if( backdrop )
{
file ~= "backdrop-";
me._label.set("fill", me._style.getColor("backdrop_fg_color"));
}
else
me._label.set("fill", me._style.getColor("fg_color"));
file ~= "button";
if( model._down )
{
file ~= "-active";
me._label.setTranslation(w / 2 + 1, h / 2 + 6);
}
else
me._label.setTranslation(w / 2, h / 2 + 5);
if( model._enabled )
{
if( model._focused and !backdrop )
file ~= "-focused";
if( model._hover and !model._down )
file ~= "-hover";
}
else
file ~= "-disabled";
me._border.set("src", file ~ ".png");
}
};
# A checbox
DefaultStyle.widgets.checkbox = {
new: func(parent, cfg)
{
me._root = parent.createChild("group", "checkbox");
me._icon =
me._root.createChild("image", "checkbox-icon")
.setSize(18, 18);
me._label =
me._root.createChild("text")
.set("font", "LiberationFonts/LiberationSans-Regular.ttf")
.set("character-size", 14)
.set("alignment", "left-center");
},
setSize: func(model, w, h)
{
me._icon.setTranslation(0, int((h - 18) / 2));
me._label.setTranslation(24, int(h / 2) + 1);
return me;
},
setText: func(model, text)
{
me._label.set("text", text);
var min_width = me._label.maxWidth() + 24;
model.setMinimumSize([min_width, 18]);
model.setSizeHint([min_width, 24]);
return me;
},
update: func(model)
{
var backdrop = !model._windowFocus();
var (w, h) = model._size;
var file = me._style._dir_widgets ~ "/";
if( backdrop )
{
file ~= "backdrop-";
me._label.set("fill", me._style.getColor("backdrop_fg_color"));
}
else
me._label.set("fill", me._style.getColor("fg_color"));
file ~= "check";
if( model._down )
file ~= "-selected";
else
file ~= "-unselected";
if( model._enabled )
{
if( model._hover )
file ~= "-hover";
}
else
file ~= "-disabled";
me._icon.set("src", file ~ ".png");
}
};
# A label
DefaultStyle.widgets.label = {
new: func(parent, cfg)
{
me._root = parent.createChild("group", "label");
},
setSize: func(model, w, h)
{
if( me['_bg'] != nil )
me._bg.reset().rect(0, 0, w, h);
if( me['_img'] != nil )
me._img.set("size[0]", w)
.set("size[1]", h);
if( me['_text'] != nil )
{
# TODO different alignment
me._text.setTranslation(2, 2 + h / 2);
me._text.set(
"max-width",
model._cfg.get("wordWrap", 0) ? (w - 4) : 0
);
}
return me;
},
setText: func(model, text)
{
if( text == nil or size(text) == 0 )
{
model.setHeightForWidthFunc(nil);
return me._deleteElement('text');
}
me._createElement("text", "text")
.set("text", text);
var hfw_func = nil;
var min_width = me._text.maxWidth() + 4;
var width_hint = min_width;
if( model._cfg.get("wordWrap", 0) )
{
var m = me;
hfw_func = func(w) m.heightForWidth(w);
min_width = math.min(32, min_width);
# prefer approximately quadratic text blocks
if( width_hint > 24 )
width_hint = int(math.sqrt(width_hint * 24));
}
model.setHeightForWidthFunc(hfw_func);
model.setMinimumSize([min_width, 14]);
model.setSizeHint([width_hint, 24]);
return me.update(model);
},
setImage: func(model, img)
{
if( img == nil or size(img) == 0 )
return me._deleteElement('img');
me._createElement("img", "image")
.set("src", img)
.set("preserveAspectRatio", "xMidYMid slice");
return me;
},
# @param bg CSS color or 'none'
setBackground: func(model, bg)
{
if( bg == nil or bg == "none" )
return me._deleteElement("bg");
me._createElement("bg", "path")
.set("fill", bg);
me.setSize(model, model._size[0], model._size[1]);
return me;
},
heightForWidth: func(w)
{
if( me['_text'] == nil )
return -1;
return math.max(14, me._text.heightForWidth(w - 4));
},
update: func(model)
{
if( me['_text'] != nil )
{
var color_name = model._windowFocus() ? "fg_color" : "backdrop_fg_color";
me._text.set("fill", me._style.getColor(color_name));
}
},
# protected:
_createElement: func(name, type)
{
var mem = '_' ~ name;
if( me[ mem ] == nil )
{
me[ mem ] = me._root.createChild(type, "label-" ~ name);
if( type == "text" )
{
me[ mem ].set("font", "LiberationFonts/LiberationSans-Regular.ttf")
.set("character-size", 14)
.set("alignment", "left-center");
}
}
return me[ mem ];
},
_deleteElement: func(name)
{
name = '_' ~ name;
if( me[ name ] != nil )
{
me[ name ].del();
me[ name ] = nil;
}
return me;
}
};
# ScrollArea
DefaultStyle.widgets["scroll-area"] = {
new: func(parent, cfg)
{
me._root = parent.createChild("group", "scroll-area");
me._bg = me._root.createChild("path", "background")
.set("fill", "#e0e0e0");
me.content = me._root.createChild("group", "scroll-content")
.set("clip-frame", Element.PARENT);
me.vert = me._newScroll(me._root, "vert");
me.horiz = me._newScroll(me._root, "horiz");
},
setColorBackground: func
{
if( size(arg) == 1 )
var arg = arg[0];
me._bg.setColorFill(arg);
},
update: func(model)
{
me.horiz.reset();
if( model._max_scroll[0] > 1 )
# only show scroll bar if horizontally scrollable
me.horiz.moveTo( model._scroller_offset[0] + model._scroller_pos[0],
model._size[1] - 2 )
.horiz(model._scroller_size[0]);
me.vert.reset();
if( model._max_scroll[1] > 1 )
# only show scroll bar if vertically scrollable
me.vert.moveTo( model._size[0] - 2,
model._scroller_offset[1] + model._scroller_pos[1] )
.vert(model._scroller_size[1]);
me._bg.reset()
.rect(0, 0, model._size[0], model._size[1]);
me.content.set(
"clip",
"rect(0, " ~ model._size[0] ~ ", " ~ model._size[1] ~ ", 0)"
);
},
# private:
_newScroll: func(el, orient)
{
return el.createChild("path", "scroll-" ~ orient)
.set("stroke", "#f07845")
.set("stroke-width", 4);
},
# Calculate size and limits of scroller
#
# @param model
# @param dir 0 for horizontal, 1 for vertical
# @return [scroller_size, min_pos, max_pos]
_updateScrollMetrics: func(model, dir)
{
if( model._content_size[dir] <= model._size[dir] )
return;
model._scroller_size[dir] =
math.max(
12,
model._size[dir] * (model._size[dir] / model._content_size[dir])
);
model._scroller_offset[dir] = 0;
model._scroller_delta[dir] = model._size[dir] - model._scroller_size[dir];
}
};