func (t *TextBox) DoubleClick(ev gxui.MouseEvent) (consume bool) { if p, ok := t.RuneIndexAt(ev.Point); ok { s, e := t.controller.WordAt(p) if ev.Modifier&gxui.ModControl != 0 { t.controller.AddSelection(gxui.CreateTextSelection(s, e, false)) } else { t.controller.SetSelection(gxui.CreateTextSelection(s, e, false)) } } t.InputEventHandler.DoubleClick(ev) return true }
func (u *Undo) Exec(target interface{}) (executed, consume bool) { finder, ok := target.(EditorFinder) if !ok { return false, false } editor := finder.CurrentEditor() if editor == nil { u.err = "undo: no file open" return true, true } history := editor.History() edit, ok := history.Undo() if !ok { u.warn = "undo: nothing to undo" return true, true } text, _ := editor.Controller().ReplaceAt(editor.Runes(), edit.At, edit.At+len(edit.New), edit.Old) editor.Controller().SetTextRunes(text) absDelta := edit.Delta if absDelta < 0 { absDelta = -absDelta } editor.Controller().SetSelection(gxui.CreateTextSelection(edit.At, edit.At+absDelta, true)) editor.ScrollToRune(edit.At) return true, true }
func (l *CodeEditorLine) PaintEditorSelections(c gxui.Canvas, info CodeEditorLinePaintInfo) { controller := l.textbox.controller ls, le := uint64(controller.LineStart(l.lineIndex)), uint64(controller.LineEnd(l.lineIndex)) selections := controller.Selections() if l.textbox.selectionDragging { interval.Replace(&selections, l.textbox.selectionDrag) } interval.Visit(&selections, gxui.CreateTextSelection(int(ls), int(le), false), func(s, e uint64, _ int) { if s < e { var startOffset math.Point if s > ls { startOffset = l.endOfChar(info.GlyphOffsets[s-ls-1]) } var endOffset math.Point if e > ls { endOffset = l.endOfChar(info.GlyphOffsets[e-ls-1]) } width := endOffset.X - startOffset.X m := l.outer.MeasureRunes(int(s), int(e)) m.W = width top := math.Point{X: l.caretWidth + startOffset.X, Y: 0} bottom := top.Add(m.Point()) l.outer.PaintSelection(c, top, bottom) } }) }
func (f *Find) Start(control gxui.Control) gxui.Control { f.editor = findEditor(control) if f.editor == nil { return nil } f.display = f.theme.CreateLabel() f.display.SetText("Start typing to search") f.pattern = newFindBox(f.driver, f.theme, f.editor) f.next = f.pattern f.pattern.OnTextChanged(func([]gxui.TextBoxEdit) { f.editor.Controller().ClearSelections() needle := f.pattern.Text() if len(needle) == 0 { f.display.SetText("Start typing to search") return } haystack := f.editor.Text() start := 0 var selections gxui.TextSelectionList for next := strings.Index(haystack, needle); next != -1; next = strings.Index(haystack[start:], needle) { start += next selection := gxui.CreateTextSelection(start, start+len(needle), false) selections = append(selections, selection) start++ } f.editor.Select(selections) f.display.SetText(fmt.Sprintf("%s: %d results found", needle, len(selections))) }) return f.display }
func (e *CodeEditor) KeyPress(ev gxui.KeyboardEvent) (consume bool) { switch ev.Key { case gxui.KeyTab: replace := true for _, sel := range e.controller.Selections() { start, end := sel.Range() if e.controller.LineIndex(start) != e.controller.LineIndex(end) { replace = false break } } switch { case replace: e.controller.ReplaceAll(strings.Repeat(" ", e.tabWidth)) e.controller.Deselect(false) case ev.Modifier.Shift(): e.controller.UnindentSelection() default: e.controller.IndentSelection() } return true case gxui.KeySpace: if ev.Modifier.Control() { e.ShowSuggestionList() return } case gxui.KeyUp: fallthrough case gxui.KeyDown: if e.IsSuggestionListShowing() { return e.suggestionList.KeyPress(ev) } case gxui.KeyLeft: e.HideSuggestionList() case gxui.KeyRight: e.HideSuggestionList() case gxui.KeyEnter: controller := e.controller if e.IsSuggestionListShowing() { text := e.suggestionAdapter.Suggestion(e.suggestionList.Selected()).Code() start, end := controller.WordAt(e.controller.LastCaret()) controller.SetSelection(gxui.CreateTextSelection(start, end, false)) controller.ReplaceAll(text) controller.Deselect(false) e.HideSuggestionList() } else { e.controller.ReplaceWithNewlineKeepIndent() } return true case gxui.KeyEscape: if e.IsSuggestionListShowing() { e.HideSuggestionList() return true } } return e.TextBox.KeyPress(ev) }
func (t *TextBox) MouseMove(ev gxui.MouseEvent) { t.List.MouseMove(ev) if t.selectionDragging { if p, ok := t.RuneIndexAt(ev.Point); ok { t.selectionDrag = gxui.CreateTextSelection(t.selectionDrag.From(), p, false) t.selectionDragging = true t.onRedrawLines.Fire() } } }
func (t *TextBox) lineMouseDown(line TextBoxLine, ev gxui.MouseEvent) { if ev.Button == gxui.MouseButtonLeft { p := line.RuneIndexAt(ev.Point) t.selectionDragging = true t.selectionDrag = gxui.CreateTextSelection(p, p, false) if !ev.Modifier.Control() { t.controller.SetCaret(p) } } }
func (t *DefaultTextBoxLine) PaintSelections(c gxui.Canvas) { controller := t.textbox.controller ls, le := controller.LineStart(t.lineIndex), controller.LineEnd(t.lineIndex) selections := controller.Selections() if t.textbox.selectionDragging { interval.Replace(&selections, t.textbox.selectionDrag) } interval.Visit(&selections, gxui.CreateTextSelection(ls, le, false), func(s, e uint64, _ int) { if s < e { x := t.outer.MeasureRunes(ls, int(s)).W m := t.outer.MeasureRunes(int(s), int(e)) top := math.Point{X: t.caretWidth + x, Y: 0} bottom := top.Add(m.Point()) t.outer.PaintSelection(c, top, bottom) } }) }
func (r *Redo) Exec(interface{}) (executed, consume bool) { if r.editor == nil { r.err = "redo: no file open" return true, true } history := r.editor.History() edit, ok := history.RedoCurrent() if !ok { r.warn = "redo: nothing to redo" return true, true } text, _ := r.editor.Controller().ReplaceAt(r.editor.Runes(), edit.At, edit.At+len(edit.Old), edit.New) r.editor.Controller().SetTextRunes(text) absDelta := edit.Delta if absDelta < 0 { absDelta = -absDelta } r.editor.Controller().SetSelection(gxui.CreateTextSelection(edit.At, edit.At+absDelta, true)) r.editor.ScrollToRune(edit.At) return true, true }
func (e *editor) KeyPress(event gxui.KeyboardEvent) bool { if event.Modifier.Control() || event.Modifier.Super() { switch event.Key { case gxui.KeySpace: e.ShowSuggestionList() return true case gxui.KeyS: if !e.lastModified.IsZero() { finfo, err := os.Stat(e.filepath) if err != nil { panic(err) } if finfo.ModTime().After(e.lastModified) { panic("Cannot save file: written since last open") } } f, err := os.Create(e.filepath) if err != nil { panic(err) } if !strings.HasSuffix(e.Text(), "\n") { e.SetText(e.Text() + "\n") } if _, err := f.WriteString(e.Text()); err != nil { panic(err) } finfo, err := f.Stat() if err != nil { panic(err) } e.lastModified = finfo.ModTime() f.Close() e.hasChanges = false return true case gxui.KeyTab: return false } } switch event.Key { case gxui.KeyTab: // TODO: Gain knowledge about scope, so we know how much to indent. switch { case event.Modifier.Shift(): e.Controller().UnindentSelection(e.TabWidth()) default: e.Controller().IndentSelection(e.TabWidth()) } return true case gxui.KeyUp: fallthrough case gxui.KeyDown: if e.IsSuggestionListShowing() { return e.suggestions.KeyPress(event) } case gxui.KeyLeft: e.HideSuggestionList() case gxui.KeyRight: e.HideSuggestionList() case gxui.KeyEnter: controller := e.Controller() if e.IsSuggestionListShowing() { text := e.adapter.Suggestion(e.suggestions.Selected()).Code() start, end := controller.WordAt(controller.LastCaret()) controller.SetSelection(gxui.CreateTextSelection(start, end, false)) controller.ReplaceAll(text) controller.Deselect(false) e.HideSuggestionList() } else { // TODO: implement electric braces. See // http://www.emacswiki.org/emacs/AutoPairs under // "Electric-RET". e.Controller().ReplaceWithNewlineKeepIndent() } return true case gxui.KeyEscape: if e.IsSuggestionListShowing() { e.HideSuggestionList() return true } } return e.CodeEditor.KeyPress(event) }
func (e *CodeEditor) KeyPress(event gxui.KeyboardEvent) bool { defer e.storePositions() if event.Modifier != 0 && event.Modifier != gxui.ModShift { // ctrl-, alt-, and cmd-/super- keybindings should be dealt // with by the commands mapped to key bindings. return false } switch event.Key { case gxui.KeyPeriod: e.ShowSuggestionList() return true case gxui.KeyBackspace: result := e.TextBox.KeyPress(event) if e.IsSuggestionListShowing() { e.SortSuggestionList() } return result case gxui.KeyPageUp, gxui.KeyPageDown, gxui.KeyDelete: // These are all bindings that the TextBox handles fine. return e.TextBox.KeyPress(event) case gxui.KeyTab: // TODO: Gain knowledge about scope, so we know how much to indent. switch { case event.Modifier.Shift(): e.Controller().UnindentSelection() default: e.Controller().IndentSelection() } return true case gxui.KeyUp, gxui.KeyDown: if e.IsSuggestionListShowing() { return e.suggestions.KeyPress(event) } return false case gxui.KeyLeft, gxui.KeyRight: if e.IsSuggestionListShowing() { e.HideSuggestionList() } return false case gxui.KeyEnter: controller := e.Controller() if e.IsSuggestionListShowing() { text := e.adapter.Suggestion(e.suggestions.Selected()).Code() start, end := controller.WordAt(controller.LastCaret()) controller.SetSelection(gxui.CreateTextSelection(start, end, false)) controller.ReplaceAll(text) controller.Deselect(false) e.HideSuggestionList() return true } // TODO: implement electric braces. See // http://www.emacswiki.org/emacs/AutoPairs under // "Electric-RET". e.Controller().ReplaceWithNewlineKeepIndent() e.ScrollToRune(e.Controller().FirstCaret()) return true case gxui.KeyEscape: if e.IsSuggestionListShowing() { e.HideSuggestionList() return true } // TODO: Keep track of some sort of concept of a "focused" caret and // focus that. e.Controller().SetCaret(e.Controller().FirstCaret()) } return false }