|
|
@@ -21,6 +21,8 @@ type Edit struct {
|
|
|
placeHolder string // place holder string
|
|
|
text string // current edit text
|
|
|
col int // current column
|
|
|
+ selStart int // start column of selection. always < selEnd. if selStart == selEnd then nothing is selected.
|
|
|
+ selEnd int // end column of selection. always > selStart. if selStart == selEnd then nothing is selected.
|
|
|
focus bool // key focus flag
|
|
|
cursorOver bool
|
|
|
blinkID int
|
|
|
@@ -63,6 +65,8 @@ func NewEdit(width int, placeHolder string) *Edit {
|
|
|
ed.text = ""
|
|
|
ed.MaxLength = 80
|
|
|
ed.col = 0
|
|
|
+ ed.selStart = 0
|
|
|
+ ed.selEnd = 0
|
|
|
ed.focus = false
|
|
|
|
|
|
ed.Label.initialize("", StyleDefault().Font)
|
|
|
@@ -80,10 +84,13 @@ func NewEdit(width int, placeHolder string) *Edit {
|
|
|
}
|
|
|
|
|
|
// SetText sets this edit text
|
|
|
-func (ed *Edit) SetText(text string) *Edit {
|
|
|
+func (ed *Edit) SetText(newText string) *Edit {
|
|
|
|
|
|
// Remove new lines from text
|
|
|
- ed.text = strings.Replace(text, "\n", "", -1)
|
|
|
+ ed.text = strings.Replace(newText, "\n", "", -1)
|
|
|
+ ed.col = text.StrCount(ed.text)
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
ed.update()
|
|
|
return ed
|
|
|
}
|
|
|
@@ -94,6 +101,28 @@ func (ed *Edit) Text() string {
|
|
|
return ed.text
|
|
|
}
|
|
|
|
|
|
+// SelectedText returns the currently selected text
|
|
|
+// or empty string when nothing is selected
|
|
|
+func (ed *Edit) SelectedText() string {
|
|
|
+
|
|
|
+ if ed.selStart == ed.selEnd {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+
|
|
|
+ s := ""
|
|
|
+ charNum := 0
|
|
|
+ for _, currentRune := range ed.text {
|
|
|
+ if charNum >= ed.selEnd {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if charNum >= ed.selStart {
|
|
|
+ s += string(currentRune)
|
|
|
+ }
|
|
|
+ charNum++
|
|
|
+ }
|
|
|
+ return s
|
|
|
+}
|
|
|
+
|
|
|
// SetFontSize sets label font size (overrides Label.SetFontSize)
|
|
|
func (ed *Edit) SetFontSize(size float64) *Edit {
|
|
|
|
|
|
@@ -124,36 +153,165 @@ func (ed *Edit) CursorPos(col int) {
|
|
|
|
|
|
if col <= text.StrCount(ed.text) {
|
|
|
ed.col = col
|
|
|
+ ed.selStart = col
|
|
|
+ ed.selEnd = col
|
|
|
ed.redraw(ed.focus)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// SetSelection selects the text between start and end
|
|
|
+func (ed *Edit) SetSelection(start, end int) {
|
|
|
+
|
|
|
+ // make sure end is bigger than start
|
|
|
+ if start > end {
|
|
|
+ start, end = end, start
|
|
|
+ }
|
|
|
+
|
|
|
+ if start < 0 {
|
|
|
+ start = 0
|
|
|
+ }
|
|
|
+ if end > text.StrCount(ed.text) {
|
|
|
+ end = text.StrCount(ed.text)
|
|
|
+ }
|
|
|
+
|
|
|
+ ed.selStart = start
|
|
|
+ ed.selEnd = end
|
|
|
+ ed.col = end
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+}
|
|
|
+
|
|
|
// CursorLeft moves the edit cursor one character left if possible
|
|
|
+// If text is selected the cursor is moved to the beginning of the selection instead
|
|
|
+// and the selection is removed
|
|
|
func (ed *Edit) CursorLeft() {
|
|
|
|
|
|
- if ed.col > 0 {
|
|
|
- ed.col--
|
|
|
+ if ed.selStart == ed.selEnd {
|
|
|
+ // no selection
|
|
|
+ // move cursor to the left if possible
|
|
|
+ if ed.col > 0 {
|
|
|
+ ed.col--
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // reset selection and move cursor to start of selection
|
|
|
+ ed.col = ed.selStart
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
ed.redraw(ed.focus)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// CursorRight moves the edit cursor one character right if possible
|
|
|
+// If text is selected the cursor is moved to the end of the selection instead
|
|
|
+// and the selection is removed
|
|
|
func (ed *Edit) CursorRight() {
|
|
|
|
|
|
- if ed.col < text.StrCount(ed.text) {
|
|
|
- ed.col++
|
|
|
+ if ed.selStart == ed.selEnd {
|
|
|
+ // no selection
|
|
|
+ // move cursor to the right if possible
|
|
|
+ if ed.col < text.StrCount(ed.text) {
|
|
|
+ ed.col++
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // reset selection and move cursor to end of selection
|
|
|
+ ed.col = ed.selEnd
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
ed.redraw(ed.focus)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// CursorBack deletes the character at left of the cursor if possible
|
|
|
-func (ed *Edit) CursorBack() {
|
|
|
+// SelectLeft expands/shrinks the selection to the left if possible
|
|
|
+func (ed *Edit) SelectLeft() {
|
|
|
|
|
|
if ed.col > 0 {
|
|
|
- ed.col--
|
|
|
- ed.text = text.StrRemove(ed.text, ed.col)
|
|
|
- ed.redraw(ed.focus)
|
|
|
- ed.Dispatch(OnChange, nil)
|
|
|
+ if ed.col == ed.selStart {
|
|
|
+ // cursor is at the start of selection
|
|
|
+ // expand selection to the left
|
|
|
+ ed.col--
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ } else {
|
|
|
+ // cursor is at the end of selection:
|
|
|
+ // remove selection from the end
|
|
|
+ ed.col--
|
|
|
+ ed.selEnd = ed.col
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// SelectRight expands/shrinks the selection to the right if possible
|
|
|
+func (ed *Edit) SelectRight() {
|
|
|
+
|
|
|
+ if ed.col < text.StrCount(ed.text) {
|
|
|
+ if ed.col == ed.selEnd {
|
|
|
+ // cursor is at the end of selection:
|
|
|
+ // expand selection to the right if possible
|
|
|
+ ed.col++
|
|
|
+ ed.selEnd = ed.col
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ } else {
|
|
|
+ // cursor is at the start of selection:
|
|
|
+ // remove selection from the start
|
|
|
+ ed.col++
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// SelectHome expands the selection to the left to the beginning of the text
|
|
|
+func (ed *Edit) SelectHome() {
|
|
|
+
|
|
|
+ if ed.selStart < ed.col {
|
|
|
+ ed.selEnd = ed.selStart
|
|
|
+ }
|
|
|
+ ed.col = 0
|
|
|
+ ed.selStart = 0
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+}
|
|
|
+
|
|
|
+// SelectEnd expands the selection to the right to the end of the text
|
|
|
+func (ed *Edit) SelectEnd() {
|
|
|
+
|
|
|
+ if ed.selEnd > ed.col {
|
|
|
+ ed.selStart = ed.selEnd
|
|
|
+ }
|
|
|
+ ed.col = text.StrCount(ed.text)
|
|
|
+ ed.selEnd = ed.col
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+}
|
|
|
+
|
|
|
+// SelectAll selects all text
|
|
|
+func (ed *Edit) SelectAll() {
|
|
|
+
|
|
|
+ ed.selStart = 0
|
|
|
+ ed.selEnd = text.StrCount(ed.text)
|
|
|
+ ed.col = ed.selEnd
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+}
|
|
|
+
|
|
|
+// CursorBack either deletes the character at left of the cursor if possible
|
|
|
+// Or if text is selected the selected text is removed all at once
|
|
|
+func (ed *Edit) CursorBack() {
|
|
|
+
|
|
|
+ if ed.selStart == ed.selEnd {
|
|
|
+ if ed.col > 0 {
|
|
|
+ ed.col--
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
+ ed.text = text.StrRemove(ed.text, ed.col)
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ ed.Dispatch(OnChange, nil)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ed.DeleteSelection()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -161,6 +319,8 @@ func (ed *Edit) CursorBack() {
|
|
|
func (ed *Edit) CursorHome() {
|
|
|
|
|
|
ed.col = 0
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
ed.redraw(ed.focus)
|
|
|
}
|
|
|
|
|
|
@@ -168,22 +328,55 @@ func (ed *Edit) CursorHome() {
|
|
|
func (ed *Edit) CursorEnd() {
|
|
|
|
|
|
ed.col = text.StrCount(ed.text)
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
ed.redraw(ed.focus)
|
|
|
}
|
|
|
|
|
|
-// CursorDelete deletes the character at the right of the cursor if possible
|
|
|
+// CursorDelete either deletes the character at the right of the cursor if possible
|
|
|
+// Or if text is selected the selected text is removed all at once
|
|
|
func (ed *Edit) CursorDelete() {
|
|
|
|
|
|
- if ed.col < text.StrCount(ed.text) {
|
|
|
- ed.text = text.StrRemove(ed.text, ed.col)
|
|
|
- ed.redraw(ed.focus)
|
|
|
+ if ed.selStart == ed.selEnd {
|
|
|
+ if ed.col < text.StrCount(ed.text) {
|
|
|
+ ed.text = text.StrRemove(ed.text, ed.col)
|
|
|
+ ed.redraw(ed.focus)
|
|
|
+ ed.Dispatch(OnChange, nil)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ed.DeleteSelection()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// DeleteSelection deletes the selected characters. Does nothing if nothing is selected.
|
|
|
+func (ed *Edit) DeleteSelection() {
|
|
|
+
|
|
|
+ if ed.selStart == ed.selEnd {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ changed := false
|
|
|
+ ed.col = ed.selStart
|
|
|
+ for ed.selEnd > ed.selStart {
|
|
|
+ if ed.col < text.StrCount(ed.text) {
|
|
|
+ changed = true
|
|
|
+ ed.text = text.StrRemove(ed.text, ed.col)
|
|
|
+ ed.selEnd--
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if changed {
|
|
|
ed.Dispatch(OnChange, nil)
|
|
|
+ ed.redraw(ed.focus)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// CursorInput inserts the specified string at the current cursor position
|
|
|
+// If text is selected the selected text gets overwritten
|
|
|
func (ed *Edit) CursorInput(s string) {
|
|
|
|
|
|
+ if ed.selStart != ed.selEnd {
|
|
|
+ ed.DeleteSelection()
|
|
|
+ }
|
|
|
if text.StrCount(ed.text) >= ed.MaxLength {
|
|
|
return
|
|
|
}
|
|
|
@@ -204,40 +397,65 @@ func (ed *Edit) CursorInput(s string) {
|
|
|
|
|
|
ed.text = newText
|
|
|
ed.col++
|
|
|
+ ed.selStart = ed.col
|
|
|
+ ed.selEnd = ed.col
|
|
|
|
|
|
ed.Dispatch(OnChange, nil)
|
|
|
ed.redraw(ed.focus)
|
|
|
}
|
|
|
|
|
|
// redraw redraws the text showing the caret if specified
|
|
|
+// the selection caret is always shown (when text is selected)
|
|
|
func (ed *Edit) redraw(caret bool) {
|
|
|
|
|
|
line := 0
|
|
|
- if !caret {
|
|
|
- line = -1
|
|
|
- }
|
|
|
- ed.Label.setTextCaret(ed.text, editMarginX, ed.width, line, ed.col)
|
|
|
+ ed.Label.setTextCaret(ed.text, editMarginX, ed.width, caret, line, ed.col, ed.selStart, ed.selEnd)
|
|
|
}
|
|
|
|
|
|
// onKey receives subscribed key events
|
|
|
func (ed *Edit) onKey(evname string, ev interface{}) {
|
|
|
|
|
|
kev := ev.(*window.KeyEvent)
|
|
|
- switch kev.Key {
|
|
|
- case window.KeyLeft:
|
|
|
- ed.CursorLeft()
|
|
|
- case window.KeyRight:
|
|
|
- ed.CursorRight()
|
|
|
- case window.KeyHome:
|
|
|
- ed.CursorHome()
|
|
|
- case window.KeyEnd:
|
|
|
- ed.CursorEnd()
|
|
|
- case window.KeyBackspace:
|
|
|
- ed.CursorBack()
|
|
|
- case window.KeyDelete:
|
|
|
- ed.CursorDelete()
|
|
|
- default:
|
|
|
- return
|
|
|
+ if kev.Mods != window.ModShift && kev.Mods != window.ModControl {
|
|
|
+ switch kev.Key {
|
|
|
+ case window.KeyLeft:
|
|
|
+ ed.CursorLeft()
|
|
|
+ case window.KeyRight:
|
|
|
+ ed.CursorRight()
|
|
|
+ case window.KeyHome:
|
|
|
+ ed.CursorHome()
|
|
|
+ case window.KeyEnd:
|
|
|
+ ed.CursorEnd()
|
|
|
+ case window.KeyBackspace:
|
|
|
+ ed.CursorBack()
|
|
|
+ case window.KeyDelete:
|
|
|
+ ed.CursorDelete()
|
|
|
+ default:
|
|
|
+ return
|
|
|
+ }
|
|
|
+ } else if kev.Mods == window.ModShift {
|
|
|
+ switch kev.Key {
|
|
|
+ case window.KeyLeft:
|
|
|
+ ed.SelectLeft()
|
|
|
+ case window.KeyRight:
|
|
|
+ ed.SelectRight()
|
|
|
+ case window.KeyHome:
|
|
|
+ ed.SelectHome()
|
|
|
+ case window.KeyEnd:
|
|
|
+ ed.SelectEnd()
|
|
|
+ case window.KeyBackspace:
|
|
|
+ ed.CursorBack()
|
|
|
+ case window.KeyDelete:
|
|
|
+ ed.SelectAll()
|
|
|
+ ed.DeleteSelection()
|
|
|
+ default:
|
|
|
+ return
|
|
|
+ }
|
|
|
+ } else if kev.Mods == window.ModControl {
|
|
|
+ switch kev.Key {
|
|
|
+ case window.KeyA:
|
|
|
+ ed.SelectAll()
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -256,9 +474,6 @@ func (ed *Edit) onMouse(evname string, ev interface{}) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- // Set key focus to this panel
|
|
|
- Manager().SetKeyFocus(ed)
|
|
|
-
|
|
|
// Find clicked column
|
|
|
var nchars int
|
|
|
for nchars = 1; nchars <= text.StrCount(ed.text); nchars++ {
|
|
|
@@ -273,6 +488,13 @@ func (ed *Edit) onMouse(evname string, ev interface{}) {
|
|
|
ed.blinkID = Manager().SetInterval(750*time.Millisecond, nil, ed.blink)
|
|
|
}
|
|
|
ed.CursorPos(nchars - 1)
|
|
|
+
|
|
|
+ // Set key focus to this panel
|
|
|
+ // Set the focus AFTER the mouse selection is handled
|
|
|
+ // Otherwise the OnFocus event would fire before the cursor is set.
|
|
|
+ // That way the OnFocus handler could NOT influence the selection
|
|
|
+ // Because it would be overridden/cleared directly afterwards.
|
|
|
+ Manager().SetKeyFocus(ed)
|
|
|
}
|
|
|
|
|
|
// onCursor receives subscribed cursor events
|
|
|
@@ -336,7 +558,7 @@ func (ed *Edit) applyStyle(s *EditStyle) {
|
|
|
|
|
|
if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
|
|
|
ed.Label.SetColor4(&s.HolderColor)
|
|
|
- ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, -1, ed.col)
|
|
|
+ ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, false, -1, ed.col, ed.selStart, ed.selEnd)
|
|
|
} else {
|
|
|
ed.Label.SetColor4(&s.FgColor)
|
|
|
ed.redraw(ed.focus)
|