canvas.gui: Add a basic LineEdit for text input.
This commit is contained in:
parent
a441c3870f
commit
246c480ea3
11 changed files with 244 additions and 5 deletions
|
@ -23,6 +23,7 @@ loadGUIFile("styles/DefaultStyle.nas");
|
||||||
loadWidget("Button");
|
loadWidget("Button");
|
||||||
loadWidget("CheckBox");
|
loadWidget("CheckBox");
|
||||||
loadWidget("Label");
|
loadWidget("Label");
|
||||||
|
loadWidget("LineEdit");
|
||||||
loadWidget("ScrollArea");
|
loadWidget("ScrollArea");
|
||||||
loadDialog("MessageBox");
|
loadDialog("MessageBox");
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,9 @@ gui.Widget = {
|
||||||
me._focused = 1;
|
me._focused = 1;
|
||||||
canvas._focused_widget = me;
|
canvas._focused_widget = me;
|
||||||
|
|
||||||
|
if( me._view != nil )
|
||||||
|
me._view._root.setFocus();
|
||||||
|
|
||||||
me._trigger("focus-in");
|
me._trigger("focus-in");
|
||||||
me._onStateChange();
|
me._onStateChange();
|
||||||
|
|
||||||
|
@ -98,6 +101,7 @@ gui.Widget = {
|
||||||
|
|
||||||
me._focused = 0;
|
me._focused = 0;
|
||||||
me.getCanvas()._focused_widget = nil;
|
me.getCanvas()._focused_widget = nil;
|
||||||
|
me.getCanvas().clearFocusElement();
|
||||||
|
|
||||||
me._trigger("focus-out");
|
me._trigger("focus-out");
|
||||||
me._onStateChange();
|
me._onStateChange();
|
||||||
|
|
|
@ -107,7 +107,7 @@ DefaultStyle.widgets.button = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
# A checbox
|
# A checkbox
|
||||||
DefaultStyle.widgets.checkbox = {
|
DefaultStyle.widgets.checkbox = {
|
||||||
new: func(parent, cfg)
|
new: func(parent, cfg)
|
||||||
{
|
{
|
||||||
|
@ -293,6 +293,96 @@ DefaultStyle.widgets.label = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# A one line text input field
|
||||||
|
DefaultStyle.widgets["line-edit"] = {
|
||||||
|
new: func(parent, cfg)
|
||||||
|
{
|
||||||
|
me._hpadding = cfg.get("hpadding", 8);
|
||||||
|
|
||||||
|
me._root = parent.createChild("group", "line-edit");
|
||||||
|
me._border =
|
||||||
|
me._root.createChild("image", "border")
|
||||||
|
.set("slice", "10 12"); #"7")
|
||||||
|
me._text =
|
||||||
|
me._root.createChild("text", "input")
|
||||||
|
.set("font", "LiberationFonts/LiberationSans-Regular.ttf")
|
||||||
|
.set("character-size", 14)
|
||||||
|
.set("alignment", "left-baseline")
|
||||||
|
.set("clip-frame", Element.PARENT);
|
||||||
|
me._cursor =
|
||||||
|
me._root.createChild("path", "cursor")
|
||||||
|
.set("stroke", "#333")
|
||||||
|
.set("stroke-width", 1)
|
||||||
|
.moveTo(me._hpadding, 5)
|
||||||
|
.vert(10);
|
||||||
|
me._hscroll = 0;
|
||||||
|
},
|
||||||
|
setSize: func(model, w, h)
|
||||||
|
{
|
||||||
|
me._border.setSize(w, h);
|
||||||
|
me._text.set(
|
||||||
|
"clip",
|
||||||
|
"rect(0, " ~ (w - me._hpadding) ~ ", " ~ h ~ ", " ~ me._hpadding ~ ")"
|
||||||
|
);
|
||||||
|
me._cursor.setDouble("coord[2]", h - 10);
|
||||||
|
|
||||||
|
return me.update(model);
|
||||||
|
},
|
||||||
|
setText: func(model, text)
|
||||||
|
{
|
||||||
|
me._text.set("text", text);
|
||||||
|
model._onStateChange();
|
||||||
|
},
|
||||||
|
update: func(model)
|
||||||
|
{
|
||||||
|
var backdrop = !model._windowFocus();
|
||||||
|
var file = me._style._dir_widgets ~ "/";
|
||||||
|
|
||||||
|
if( backdrop )
|
||||||
|
file ~= "backdrop-";
|
||||||
|
|
||||||
|
file ~= "entry";
|
||||||
|
|
||||||
|
if( !model._enabled )
|
||||||
|
file ~= "-disabled";
|
||||||
|
else if( model._focused and !backdrop )
|
||||||
|
file ~= "-focused";
|
||||||
|
|
||||||
|
me._border.set("src", file ~ ".png");
|
||||||
|
|
||||||
|
var color_name = backdrop ? "backdrop_fg_color" : "fg_color";
|
||||||
|
me._text.set("fill", me._style.getColor(color_name));
|
||||||
|
|
||||||
|
me._cursor.setVisible(model._enabled and model._focused and !backdrop);
|
||||||
|
|
||||||
|
var width = model._size[0] - 2 * me._hpadding;
|
||||||
|
var cursor_pos = me._text.getCursorPos(0, model._cursor)[0];
|
||||||
|
var text_width = me._text.getCursorPos(0, me._text.lineLength(0))[0];
|
||||||
|
|
||||||
|
if( text_width <= width )
|
||||||
|
# fit -> align left (TODO handle different alignment)
|
||||||
|
me._hscroll = 0;
|
||||||
|
else if( me._hscroll + cursor_pos > width )
|
||||||
|
# does not fit, cursor to the right
|
||||||
|
me._hscroll = width - cursor_pos;
|
||||||
|
else if( me._hscroll + cursor_pos < 0 )
|
||||||
|
# does not fit, cursor to the left
|
||||||
|
me._hscroll = -cursor_pos;
|
||||||
|
else if( me._hscroll + text_width < width )
|
||||||
|
# does not fit, limit scroll to align with right side
|
||||||
|
me._hscroll = width - text_width;
|
||||||
|
|
||||||
|
var text_pos = me._hscroll + me._hpadding;
|
||||||
|
|
||||||
|
me._text
|
||||||
|
.setTranslation(text_pos, model._size[1] / 2 + 5)
|
||||||
|
.update();
|
||||||
|
me._cursor
|
||||||
|
.setDouble("coord[0]", text_pos + cursor_pos)
|
||||||
|
.update();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
# ScrollArea
|
# ScrollArea
|
||||||
DefaultStyle.widgets["scroll-area"] = {
|
DefaultStyle.widgets["scroll-area"] = {
|
||||||
new: func(parent, cfg)
|
new: func(parent, cfg)
|
||||||
|
|
|
@ -57,6 +57,8 @@ gui.widgets.Button = {
|
||||||
# protected:
|
# protected:
|
||||||
_setView: func(view)
|
_setView: func(view)
|
||||||
{
|
{
|
||||||
|
call(gui.Widget._setView, [view], me);
|
||||||
|
|
||||||
var el = view._root;
|
var el = view._root;
|
||||||
el.addEventListener("mousedown", func if( me._enabled ) me.setDown(1));
|
el.addEventListener("mousedown", func if( me._enabled ) me.setDown(1));
|
||||||
el.addEventListener("mouseup", func if( me._enabled ) me.setDown(0));
|
el.addEventListener("mouseup", func if( me._enabled ) me.setDown(0));
|
||||||
|
@ -64,7 +66,5 @@ gui.widgets.Button = {
|
||||||
|
|
||||||
el.addEventListener("mouseleave",func me.setDown(0));
|
el.addEventListener("mouseleave",func me.setDown(0));
|
||||||
el.addEventListener("drag", func(e) e.stopPropagation());
|
el.addEventListener("drag", func(e) e.stopPropagation());
|
||||||
|
|
||||||
call(gui.Widget._setView, [view], me);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
144
Nasal/canvas/gui/widgets/LineEdit.nas
Normal file
144
Nasal/canvas/gui/widgets/LineEdit.nas
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
gui.widgets.LineEdit = {
|
||||||
|
new: func(parent, style, cfg)
|
||||||
|
{
|
||||||
|
var m = gui.Widget.new(gui.widgets.LineEdit);
|
||||||
|
m._cfg = Config.new(cfg);
|
||||||
|
m._focus_policy = m.StrongFocus;
|
||||||
|
m._setView( style.createWidget(parent, "line-edit", m._cfg) );
|
||||||
|
|
||||||
|
m.setLayoutMinimumSize([28, 16]);
|
||||||
|
m.setLayoutSizeHint([150, 28]);
|
||||||
|
|
||||||
|
m._text = "";
|
||||||
|
m._max_length = 32767;
|
||||||
|
m._cursor = 0;
|
||||||
|
m._selection_start = 0;
|
||||||
|
m._selection_end = 0;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
setMaxLength: func(len)
|
||||||
|
{
|
||||||
|
me._max_length = len;
|
||||||
|
|
||||||
|
if( utf8.size(me._text) <= len )
|
||||||
|
return;
|
||||||
|
|
||||||
|
me._text = utf8.substr(me._text, 0, me._max_length);
|
||||||
|
me.moveCursor(me._cursor);
|
||||||
|
},
|
||||||
|
moveCursor: func(pos, mark = 0)
|
||||||
|
{
|
||||||
|
var len = utf8.size(me._text);
|
||||||
|
me._cursor = math.max(0, math.min(pos, len));
|
||||||
|
|
||||||
|
me._selection_start = me._cursor;
|
||||||
|
me._selection_end = me._cursor;
|
||||||
|
|
||||||
|
me._onStateChange();
|
||||||
|
},
|
||||||
|
home: func()
|
||||||
|
{
|
||||||
|
me.moveCursor(0);
|
||||||
|
},
|
||||||
|
end: func()
|
||||||
|
{
|
||||||
|
me.moveCursor(utf8.size(me._text));
|
||||||
|
},
|
||||||
|
# Insert given text after cursor (and first remove selection if set)
|
||||||
|
insert: func(text)
|
||||||
|
{
|
||||||
|
var after = utf8.substr(me._text, me._selection_end);
|
||||||
|
me._text = utf8.substr(me._text, 0, me._selection_start);
|
||||||
|
|
||||||
|
# Replace selected text, insert new text and place cursor after inserted
|
||||||
|
# text
|
||||||
|
var remaining = me._max_length - me._selection_start - utf8.size(after);
|
||||||
|
if( remaining != 0 )
|
||||||
|
me._text ~= utf8.substr(text, 0, remaining);
|
||||||
|
|
||||||
|
me._cursor = utf8.size(me._text);
|
||||||
|
me._selection_start = me._cursor;
|
||||||
|
me._selection_end = me._cursor;
|
||||||
|
|
||||||
|
me._text ~= after;
|
||||||
|
|
||||||
|
if( me._view != nil )
|
||||||
|
me._view.setText(me, me._text);
|
||||||
|
},
|
||||||
|
paste: func(mode = nil)
|
||||||
|
{
|
||||||
|
me.insert(clipboard.getText(mode != nil ? mode : clipboard.CLIPBOARD));
|
||||||
|
},
|
||||||
|
# Remove selected text
|
||||||
|
removeSelection: func()
|
||||||
|
{
|
||||||
|
if( me._selection_start == me._selection_end )
|
||||||
|
return;
|
||||||
|
|
||||||
|
me._text = utf8.substr(me._text, 0, me._selection_start)
|
||||||
|
~ utf8.substr(me._text, me._selection_end);
|
||||||
|
|
||||||
|
me._cursor = me._selection_start;
|
||||||
|
me._selection_end = me._selection_start;
|
||||||
|
|
||||||
|
if( me._view != nil )
|
||||||
|
me._view.setText(me, me._text);
|
||||||
|
},
|
||||||
|
# Remove selection or if nothing is selected the character before the cursor
|
||||||
|
backspace: func()
|
||||||
|
{
|
||||||
|
if( me._selection_start == me._selection_end )
|
||||||
|
{
|
||||||
|
if( me._selection_start == 0 )
|
||||||
|
# Before first character...
|
||||||
|
return;
|
||||||
|
|
||||||
|
me._selection_start -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
me.removeSelection();
|
||||||
|
},
|
||||||
|
# Remove selection or if nothing is selected the character after the cursor
|
||||||
|
del: func()
|
||||||
|
{
|
||||||
|
if( me._selection_start == me._selection_end )
|
||||||
|
{
|
||||||
|
if( me._selection_end == utf8.size(me._text) )
|
||||||
|
# After last character...
|
||||||
|
return;
|
||||||
|
|
||||||
|
me._selection_end += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
me.removeSelection();
|
||||||
|
},
|
||||||
|
# protected:
|
||||||
|
_setView: func(view)
|
||||||
|
{
|
||||||
|
call(gui.Widget._setView, [view], me);
|
||||||
|
|
||||||
|
var el = view._root;
|
||||||
|
el.addEventListener("keypress", func (e) me.insert(e.key));
|
||||||
|
el.addEventListener("keydown", func (e)
|
||||||
|
{
|
||||||
|
if( me._view == nil )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( e.key == "Backspace" )
|
||||||
|
me.backspace();
|
||||||
|
else if( e.key == "Delete" )
|
||||||
|
me.del();
|
||||||
|
else if( e.key == "Left" )
|
||||||
|
me.moveCursor(me._cursor - 1);
|
||||||
|
else if( e.key == "Right")
|
||||||
|
me.moveCursor(me._cursor + 1);
|
||||||
|
else if( e.key == "Home" )
|
||||||
|
me.home();
|
||||||
|
else if( e.key == "End" )
|
||||||
|
me.end();
|
||||||
|
else if( e.keyCode == `v` and e.ctrlKey )
|
||||||
|
me.paste();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -119,6 +119,8 @@ gui.widgets.ScrollArea = {
|
||||||
# protected:
|
# protected:
|
||||||
_setView: func(view)
|
_setView: func(view)
|
||||||
{
|
{
|
||||||
|
call(gui.Widget._setView, [view], me);
|
||||||
|
|
||||||
view.vert.addEventListener("mousedown", func(e) me._dragStart(e));
|
view.vert.addEventListener("mousedown", func(e) me._dragStart(e));
|
||||||
view.horiz.addEventListener("mousedown", func(e) me._dragStart(e));
|
view.horiz.addEventListener("mousedown", func(e) me._dragStart(e));
|
||||||
view._root.addEventListener("mousedown", func(e)
|
view._root.addEventListener("mousedown", func(e)
|
||||||
|
@ -177,8 +179,6 @@ gui.widgets.ScrollArea = {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
call(gui.Widget._setView, [view], me);
|
|
||||||
},
|
},
|
||||||
_dragStart: func(e)
|
_dragStart: func(e)
|
||||||
{
|
{
|
||||||
|
|
BIN
gui/styles/AmbianceClassic/widgets/backdrop-entry-disabled.png
Normal file
BIN
gui/styles/AmbianceClassic/widgets/backdrop-entry-disabled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 527 B |
BIN
gui/styles/AmbianceClassic/widgets/backdrop-entry.png
Normal file
BIN
gui/styles/AmbianceClassic/widgets/backdrop-entry.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 675 B |
BIN
gui/styles/AmbianceClassic/widgets/entry-disabled.png
Normal file
BIN
gui/styles/AmbianceClassic/widgets/entry-disabled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 579 B |
BIN
gui/styles/AmbianceClassic/widgets/entry-focused.png
Normal file
BIN
gui/styles/AmbianceClassic/widgets/entry-focused.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 977 B |
BIN
gui/styles/AmbianceClassic/widgets/entry.png
Normal file
BIN
gui/styles/AmbianceClassic/widgets/entry.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 695 B |
Loading…
Reference in a new issue