LineEdit: selection work
Implemented line edit selection highlighting and Shift+arrow keys, mouse dragging, and mouse double-clicking text selection
This commit is contained in:
parent
8b0da63e7c
commit
8277a6d608
5 changed files with 213 additions and 51 deletions
|
@ -92,7 +92,7 @@ var WidgetsFactoryDialog = {
|
|||
|
||||
m.button = gui.widgets.Button.new(m.tabsContent, style, {})
|
||||
.setText("A button")
|
||||
.setFixedSize(60, 30)
|
||||
.setFixedSize(128, 30)
|
||||
.listen("clicked", func {
|
||||
InputDialog.getText("You clicked the button …", "Enter some text:", func (button, text) {
|
||||
MessageBox.information("You clicked the button …", "… and entered '" ~ (text != nil ? text : "nothing") ~ "' !");
|
||||
|
@ -124,7 +124,7 @@ var WidgetsFactoryDialog = {
|
|||
.setCheckable(1)
|
||||
.setChecked(0)
|
||||
.setText("Checkable button")
|
||||
.setFixedSize(120, 30)
|
||||
.setFixedSize(128, 30)
|
||||
.listen("toggled", func (e) {
|
||||
m.image.setVisible(int(e.detail.checked));
|
||||
});
|
||||
|
@ -132,7 +132,7 @@ var WidgetsFactoryDialog = {
|
|||
|
||||
m.upsize_button = gui.widgets.Button.new(m.tabsContent, style, {})
|
||||
.setText("Upsize window")
|
||||
.setFixedSize(130, 30)
|
||||
.setFixedSize(128, 30)
|
||||
.listen("clicked", func {
|
||||
var s = m.window.getSize();
|
||||
m.window.setSize(s[0] + 100, s[1] + 100);
|
||||
|
@ -141,12 +141,19 @@ var WidgetsFactoryDialog = {
|
|||
|
||||
m.downsize_button = gui.widgets.Button.new(m.tabsContent, style, {})
|
||||
.setText("Downsize window")
|
||||
.setFixedSize(130, 30)
|
||||
.setFixedSize(128, 30)
|
||||
.listen("clicked", func {
|
||||
var s = m.window.getSize();
|
||||
m.window.setSize(s[0] - 100, s[1] - 100);
|
||||
});
|
||||
m.button_box.addItem(m.downsize_button);
|
||||
m.combo1 = gui.widgets.ComboBox.new(m.tabsContent, style, {});
|
||||
m.combo1.addMenuItem("Apples", 0);
|
||||
m.combo1.addMenuItem("Pears", 1);
|
||||
m.combo1.addMenuItem("Lemons", 2);
|
||||
m.combo1.addMenuItem("Oranges", 3);
|
||||
m.combo1.setFixedSize(128, 30);
|
||||
m.button_box.addItem(m.combo1);
|
||||
|
||||
m.switch_box = HBoxLayout.new();
|
||||
m.button_box.addItem(m.switch_box);
|
||||
|
@ -201,13 +208,6 @@ var WidgetsFactoryDialog = {
|
|||
m.numericControlsTab.addItem(m.slider);
|
||||
|
||||
|
||||
m.combo1 = gui.widgets.ComboBox.new(m.tabsContent, style, {});
|
||||
m.combo1.addMenuItem("Apples", 0);
|
||||
m.combo1.addMenuItem("Pears", 1);
|
||||
m.combo1.addMenuItem("Lemons", 2);
|
||||
m.combo1.addMenuItem("Oranges", 3);
|
||||
|
||||
m.numericControlsTab.addItem(m.combo1);
|
||||
return m;
|
||||
},
|
||||
|
||||
|
|
|
@ -415,6 +415,15 @@ DefaultStyle.widgets["line-edit"] = {
|
|||
.set("character-size", 14)
|
||||
.set("alignment", "left-baseline")
|
||||
.set("clip-frame", Element.PARENT);
|
||||
me._selection = me._root.createChild("path", "selection")
|
||||
.set("clip-frame", Element.PARENT)
|
||||
.set("fill", "#3333ff");
|
||||
me._selected_text = me._root.createChild("text", "selected-text")
|
||||
.set("font", "LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.set("character-size", 14)
|
||||
.set("alignment", "left-baseline")
|
||||
.set("clip-frame", Element.PARENT)
|
||||
.set("fill", "#ffffff");
|
||||
me._cursor =
|
||||
me._root.createChild("path", "cursor")
|
||||
.set("stroke", "#333")
|
||||
|
@ -422,6 +431,13 @@ DefaultStyle.widgets["line-edit"] = {
|
|||
.moveTo(me._hpadding, 5)
|
||||
.vert(10);
|
||||
me._hscroll = 0;
|
||||
me._cursor_blink = 1;
|
||||
me._cursor_blink_timer = maketimer(0.5, func {
|
||||
me._cursor_blink = !me._cursor_blink;
|
||||
me._cursor.setVisible(me._cursor_visible and me._cursor_blink);
|
||||
});
|
||||
me._cursor_blink_timer.simulatedTime = 0;
|
||||
me._cursor_blink_timer.start();
|
||||
},
|
||||
setSize: func(model, w, h)
|
||||
{
|
||||
|
@ -430,6 +446,14 @@ DefaultStyle.widgets["line-edit"] = {
|
|||
"clip",
|
||||
"rect(0, " ~ (w - me._hpadding) ~ ", " ~ h ~ ", " ~ me._hpadding ~ ")"
|
||||
);
|
||||
me._selected_text.set(
|
||||
"clip",
|
||||
"rect(0, " ~ (w - me._hpadding) ~ ", " ~ h ~ ", " ~ me._hpadding ~ ")"
|
||||
);
|
||||
me._selection.set(
|
||||
"clip",
|
||||
"rect(0, " ~ (w - me._hpadding) ~ ", " ~ h ~ ", " ~ me._hpadding ~ ")"
|
||||
);
|
||||
me._cursor.setDouble("coord[2]", h - 10);
|
||||
|
||||
return me.update(model);
|
||||
|
@ -458,8 +482,17 @@ DefaultStyle.widgets["line-edit"] = {
|
|||
|
||||
var color_name = backdrop ? "backdrop_fg_color" : "fg_color";
|
||||
me._text.set("fill", me._style.getColor(color_name));
|
||||
me._selected_text.set("fill", me._style.getColor("text_color_selected"));
|
||||
me._selection.set("fill", me._style.getColor((backdrop ? "backdrop_" : "") ~ "text_color_bg_selected"));
|
||||
|
||||
me._cursor.setVisible(model._enabled and model._focused and !backdrop);
|
||||
me._cursor_visible = model._enabled and model._focused and !backdrop and model._selection_start == model._selection_end;
|
||||
me._cursor.setVisible(me._cursor_visible and me._cursor_blink);
|
||||
me._selection.reset()
|
||||
.moveTo(me._text.getCursorPos(0, model._selection_start)[0], 0)
|
||||
.vert(16)
|
||||
.horizTo(me._text.getCursorPos(0, model._selection_end)[0])
|
||||
.vert(-16);
|
||||
me._selected_text.setText(model.selectedText());
|
||||
|
||||
var width = model._size[0] - 2 * me._hpadding;
|
||||
var cursor_pos = me._text.getCursorPos(0, model._cursor)[0];
|
||||
|
@ -486,6 +519,10 @@ DefaultStyle.widgets["line-edit"] = {
|
|||
me._cursor
|
||||
.setDouble("coord[0]", text_pos + cursor_pos)
|
||||
.update();
|
||||
me._selection.setTranslation(text_pos, model._size[1] / 2 - 8)
|
||||
.update();
|
||||
me._selected_text.setTranslation(text_pos + me._text.getCursorPos(0, model._selection_start)[0], model._size[1] / 2 + 5)
|
||||
.update();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1198,6 +1235,7 @@ DefaultStyle.widgets["list-item"] = {
|
|||
var min_width = m + me._label.maxWidth() + m;
|
||||
model.setLayoutMinimumSize([min_width, me._itemHeight]);
|
||||
model.setLayoutSizeHint([min_width, me._itemHeight]);
|
||||
model.setLayoutMaximumSize([model._MAX_SIZE, me._itemHeight]);
|
||||
|
||||
return me;
|
||||
},
|
||||
|
|
|
@ -37,11 +37,11 @@ gui.widgets.LineEdit = {
|
|||
|
||||
me._text = utf8.substr(text, 0, me._max_length);
|
||||
me._cursor = utf8.size(me._text);
|
||||
me._selection_start = me._cursor;
|
||||
me._selection_end = me._cursor;
|
||||
me.clearSelection();
|
||||
|
||||
if( me._view != nil )
|
||||
me._view.setText(me, me._text);
|
||||
me._trigger("text-changed");
|
||||
|
||||
return me;
|
||||
},
|
||||
|
@ -49,11 +49,12 @@ gui.widgets.LineEdit = {
|
|||
{
|
||||
me._text = "";
|
||||
me._cursor = 0;
|
||||
me._selection_start = 0;
|
||||
me._selection_end = 0;
|
||||
me.clearSelection();
|
||||
|
||||
if( me._view != nil )
|
||||
me._view.setText(me, "");
|
||||
me._trigger("text-changed");
|
||||
me._onStateChange();
|
||||
},
|
||||
text: func()
|
||||
{
|
||||
|
@ -66,10 +67,14 @@ gui.widgets.LineEdit = {
|
|||
{
|
||||
me._max_length = len;
|
||||
|
||||
if( utf8.size(me._text) <= len )
|
||||
if (utf8.size(me._text) <= len) {
|
||||
return me;
|
||||
}
|
||||
|
||||
me._text = utf8.substr(me._text, 0, me._max_length);
|
||||
if( me._view != nil )
|
||||
me._view.setText(me, "");
|
||||
me._trigger("text-changed");
|
||||
me.moveCursor(me._cursor);
|
||||
return me;
|
||||
},
|
||||
|
@ -78,41 +83,71 @@ gui.widgets.LineEdit = {
|
|||
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();
|
||||
return me;
|
||||
},
|
||||
clearSelection: func {
|
||||
me._selection_start = me._selection_end = 0;
|
||||
me._onStateChange();
|
||||
},
|
||||
setSelection: func(start, end) {
|
||||
me._selection_start = start;
|
||||
me._selection_end = end;
|
||||
me._onStateChange();
|
||||
},
|
||||
getSelection: func {
|
||||
if (me._selection_start != me._selection_end) {
|
||||
return [me._selection_start, me._selection_end];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
},
|
||||
_getNearestCursorPos: func(x) {
|
||||
var crs = me._getNearestCursor(x);
|
||||
return me._view._text.getCursorPos(crs[0], crs[1]);
|
||||
},
|
||||
_getNearestCursor: func(x) {
|
||||
return me._view._text.getNearestCursor([x, 5]);
|
||||
},
|
||||
moveCursorX: func(x) {
|
||||
me._cursor = me._getNearestCursor(x)[1];
|
||||
me._onStateChange();
|
||||
return me;
|
||||
},
|
||||
home: func()
|
||||
{
|
||||
me.moveCursor(0);
|
||||
me.clearSelection();
|
||||
},
|
||||
end: func()
|
||||
{
|
||||
me.moveCursor(utf8.size(me._text));
|
||||
me.clearSelection();
|
||||
},
|
||||
# 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);
|
||||
var after = utf8.substr(me._text, me._cursor);
|
||||
me._text = utf8.substr(me._text, 0, me._cursor);
|
||||
|
||||
# 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 )
|
||||
var remaining = me._max_length - me._cursor - utf8.size(after);
|
||||
if (remaining > 0) {
|
||||
me._text ~= utf8.substr(text, 0, remaining);
|
||||
}
|
||||
|
||||
#me.clearSelection();
|
||||
me._cursor = utf8.size(me._text);
|
||||
me._selection_start = me._cursor;
|
||||
me._selection_end = me._cursor;
|
||||
|
||||
me._text ~= after;
|
||||
|
||||
if( me._view != nil )
|
||||
if (me._view != nil) {
|
||||
me._view.setText(me, me._text);
|
||||
}
|
||||
me._trigger("text-changed");
|
||||
|
||||
me._onStateChange();
|
||||
return me;
|
||||
},
|
||||
copy: func() {
|
||||
|
@ -127,36 +162,40 @@ gui.widgets.LineEdit = {
|
|||
me.insert(clipboard.getText(mode != nil ? mode : clipboard.CLIPBOARD));
|
||||
},
|
||||
selectAll: func() {
|
||||
me._selection_start = 0;
|
||||
me._selection_end = utf8.size(me._text) - 1;
|
||||
me.setSelection(0, utf8.size(me._text));
|
||||
},
|
||||
# Remove selected text
|
||||
removeSelection: func()
|
||||
{
|
||||
if( me._selection_start == me._selection_end )
|
||||
if (me._selection_start == me._selection_end) {
|
||||
me._selection_start = me._selection_end = 0;
|
||||
return me;
|
||||
}
|
||||
|
||||
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;
|
||||
me.clearSelection();
|
||||
|
||||
if( me._view != nil )
|
||||
if (me._view != nil) {
|
||||
me._view.setText(me, me._text);
|
||||
}
|
||||
me._trigger("text-changed");
|
||||
|
||||
return me;
|
||||
me._onStateChange();
|
||||
return me
|
||||
},
|
||||
# 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 )
|
||||
if (me._selection_start == me._selection_end) {
|
||||
if (me._cursor == 0) {
|
||||
# Before first character...
|
||||
return me;
|
||||
|
||||
me._selection_start -= 1;
|
||||
}
|
||||
me._selection_start = me._cursor - 1;
|
||||
me._selection_end = me._cursor;
|
||||
}
|
||||
|
||||
me.removeSelection();
|
||||
|
@ -165,13 +204,14 @@ gui.widgets.LineEdit = {
|
|||
# Remove selection or if nothing is selected the character after the cursor
|
||||
delete: func()
|
||||
{
|
||||
if( me._selection_start == me._selection_end )
|
||||
{
|
||||
if( me._selection_end == utf8.size(me._text) )
|
||||
if (me._selection_start == me._selection_end) {
|
||||
if (me._cursor == utf8.size(me._text)) {
|
||||
# After last character...
|
||||
return me;
|
||||
}
|
||||
|
||||
me._selection_end += 1;
|
||||
me._selection_start = me._cursor;
|
||||
me._selection_end = me._cursor + 1;
|
||||
}
|
||||
|
||||
me.removeSelection();
|
||||
|
@ -185,6 +225,7 @@ gui.widgets.LineEdit = {
|
|||
var el = view._root;
|
||||
el.addEventListener("keypress", func (e) {
|
||||
if (!e.ctrlKey and !e.altKey and !e.metaKey) {
|
||||
me.removeSelection();
|
||||
me.insert(e.key);
|
||||
}
|
||||
});
|
||||
|
@ -193,28 +234,90 @@ gui.widgets.LineEdit = {
|
|||
if( me._view == nil )
|
||||
return;
|
||||
|
||||
if( e.key == "Enter" )
|
||||
if (e.key == "Enter") {
|
||||
me._trigger("editingFinished", {text: me.text()}); # TODO validator/etc.
|
||||
else if( e.key == "Backspace" )
|
||||
} elsif (e.key == "Backspace") {
|
||||
me.backspace();
|
||||
else if( e.key == "Delete" )
|
||||
} elsif (e.key == "Delete") {
|
||||
me.delete();
|
||||
else if( e.key == "Left" )
|
||||
} elsif (e.key == "Left") {
|
||||
if (e.shiftKey) {
|
||||
if (me._selection_start == 0 and me._selection_end == 0) {
|
||||
var start = me._cursor;
|
||||
var end = me._cursor;
|
||||
} else {
|
||||
var start = me._selection_start;
|
||||
var end = me._selection_end;
|
||||
}
|
||||
if (start > 0) {
|
||||
me.setSelection(start - 1, end);
|
||||
}
|
||||
} else {
|
||||
if (me._selection_start != 0 or me._selection_end != 0) {
|
||||
me.moveCursor(me._selection_start);
|
||||
me.clearSelection();
|
||||
} else {
|
||||
me.moveCursor(me._cursor - 1);
|
||||
else if( e.key == "Right")
|
||||
}
|
||||
}
|
||||
} elsif (e.key == "Right") {
|
||||
if (e.shiftKey) {
|
||||
if (me._selection_start == 0 and me._selection_end == 0) {
|
||||
var start = me._cursor;
|
||||
var end = me._cursor;
|
||||
} else {
|
||||
var start = me._selection_start;
|
||||
var end = me._selection_end;
|
||||
}
|
||||
if (end + 1< utf8.size(me._text)) {
|
||||
me.setSelection(start, end + 1);
|
||||
}
|
||||
} else {
|
||||
if (me._selection_end != 0 or me._selection_start != 0) {
|
||||
me.moveCursor(me._selection_end);
|
||||
me.clearSelection();
|
||||
} else {
|
||||
me.moveCursor(me._cursor + 1);
|
||||
else if( e.key == "Home" )
|
||||
}
|
||||
}
|
||||
} elsif (e.key == "Home") {
|
||||
me.home();
|
||||
else if( e.key == "End" )
|
||||
} elsif (e.key == "End") {
|
||||
me.end();
|
||||
}
|
||||
});
|
||||
el.addEventListener("click", func(e) {
|
||||
if (e.button == 2) {
|
||||
me.showContextMenu(e);
|
||||
} elsif (e.button == 0) {
|
||||
me.clearSelection();
|
||||
me.moveCursorX(e.localX - view._text.getTranslation()[0]);
|
||||
}
|
||||
});
|
||||
el.addEventListener("dblclick", func(e) {
|
||||
me.selectAll();
|
||||
});
|
||||
el.addEventListener("drag", func(e) {
|
||||
var pos = me._getNearestCursor(e.localX - view._text.getTranslation()[0])[1];
|
||||
if (me._selection_start < pos and me._selection_end > pos) { # dragging within existing selection
|
||||
# TODO: implement full drag / drop support
|
||||
} elsif (me._selection_start != me._selection_end) { # existing selection, but dragging outside
|
||||
if (e.deltaX < 0) {
|
||||
me.setSelection(pos, me._selection_end);
|
||||
} elsif (e.deltaX > 0) {
|
||||
me.setSelection(me._selection_start, pos);
|
||||
}
|
||||
} else { # no existing selection, create one from drag position
|
||||
if (math.abs(e.deltaX) > 1) {
|
||||
var start = pos;
|
||||
var end = pos + math.sgn(e.deltaX);
|
||||
me.setSelection(math.min(start, end), math.max(start, end));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
del: func() {
|
||||
me.context_menu.del();
|
||||
me._view._cursor_blink_timer.stop();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -353,6 +353,9 @@ var Shortcut = {
|
|||
},
|
||||
|
||||
match: func(keys, shift=0, ctrl=0, alt=0, meta=0) {
|
||||
if (typeof(keys) != "vector") {
|
||||
keys = [keys];
|
||||
}
|
||||
if (!me.modifiers and !me.keys) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,24 @@
|
|||
<blue type="float">0.235</blue>
|
||||
</text_color>
|
||||
|
||||
<text_color_selected>
|
||||
<red type="float">1</red>
|
||||
<green type="float">1</green>
|
||||
<blue type="float">1</blue>
|
||||
</text_color_selected>
|
||||
|
||||
<text_color_bg_selected>
|
||||
<red type="float">0.3</red>
|
||||
<green type="float">0.3</green>
|
||||
<blue type="float">1</blue>
|
||||
</text_color_bg_selected>
|
||||
|
||||
<backdrop_text_color_bg_selected>
|
||||
<red type="float">0.5</red>
|
||||
<green type="float">0.5</green>
|
||||
<blue type="float">0.5</blue>
|
||||
</backdrop_text_color_bg_selected>
|
||||
|
||||
<backdrop_fg_color>
|
||||
<red type="float">0.428</red>
|
||||
<green type="float">0.427</green>
|
||||
|
|
Loading…
Reference in a new issue