// Called when a new view is opened func (f *frontend) onNew(bv *backend.View) { w := f.windows[bv.Window()] v := newView(bv) w.views[bv] = v if w.qw != nil { w.qw.Call("addTab", v.id, v) w.qw.Call("activateTab", v.id) } }
// called when a view has loaded func (f *frontend) onLoad(bv *backend.View) { w := f.windows[bv.Window()] v := w.views[bv] if v == nil { log.Error("Couldn't find loaded view") return } v.Title = bv.FileName() w.qw.Call("setTabTitle", v.id, v.Title) }
// called when a view is closed func (f *frontend) onClose(bv *backend.View) { w := f.windows[bv.Window()] v := w.views[bv] if v == nil { log.Error("Couldn't find closed view...") return } w.qw.Call("removeTab", v.id) delete(w.views, bv) }
func (f *frontend) onStatusChanged(bv *backend.View) { w := f.windows[bv.Window()] v := w.views[bv] if v == nil { log.Error("Couldn't find status changed view") return } if v.qv == nil { return } v.qv.Call("onStatusChanged") }
func (f *frontend) onSelectionModified(bv *backend.View) { w := f.windows[bv.Window()] v := w.views[bv] if v == nil { log.Error("Couldn't find modified view") return } if v.qv == nil { return } v.qv.Call("onSelectionModified") }
func (t *tbfe) VisibleRegion(v *backend.View) Region { t.lock.Lock() r, ok := t.layout[v] t.lock.Unlock() if !ok || r.lastUpdate != v.ChangeCount() { t.Show(v, r.visible) t.lock.Lock() r = t.layout[v] t.lock.Unlock() } return r.visible }
func (t *tbfe) Show(v *backend.View, r Region) { t.lock.Lock() l := t.layout[v] t.lock.Unlock() p := util.Prof.Enter("show") defer p.Exit() lv := l.visible s1, _ := v.RowCol(lv.Begin()) e1, _ := v.RowCol(lv.End()) s2, _ := v.RowCol(r.Begin()) e2, _ := v.RowCol(r.End()) r1 := Region{s1, e1} r2 := Region{s2, e2} r3 := r1.Cover(r2) diff := 0 if d1, d2 := Abs(r1.Begin()-r3.Begin()), Abs(r1.End()-r3.End()); d1 > d2 { diff = r3.Begin() - r1.Begin() } else { diff = r3.End() - r1.End() } r3.A = r1.Begin() + diff r3.B = r1.End() + diff r3 = t.clip(v, r3.A, r3.B) l.visible = r3 t.lock.Lock() t.layout[v] = l t.lock.Unlock() t.render() }
func (t *tbfe) setupCallbacks(view *backend.View) { // Ensure that the visible region currently presented is // inclusive of the insert/erase delta. view.AddObserver(&tbfeBufferDeltaObserver{t: t, view: view}) backend.OnNew.Add(func(v *backend.View) { v.Settings().AddOnChange("lime.frontend.termbox.render", func(name string) { t.render() }) }) backend.OnModified.Add(func(v *backend.View) { t.render() }) backend.OnSelectionModified.Add(func(v *backend.View) { t.render() }) }
func (t *tbfe) clip(v *backend.View, s, e int) Region { p := util.Prof.Enter("clip") defer p.Exit() t.lock.Lock() h := t.layout[v].height t.lock.Unlock() if e-s > h { e = s + h } else if e-s < h { s = e - h } if e2, _ := v.RowCol(v.TextPoint(e, 0)); e2 < e { e = e2 } if s < 0 { s = 0 } e = s + h r := Region{v.TextPoint(s, 0), v.TextPoint(e, 0)} return v.LineR(r) }
func (t *tbfe) renderLStatus(v *backend.View, y int, fg, bg termbox.Attribute) { st := v.Status() sel := v.Sel() j := 0 for k, v := range st { s := fmt.Sprintf("%s: %s, ", k, v) addString(j, y, s, fg, bg) } if sel.Len() == 0 { return } else if l := sel.Len(); l > 1 { s := fmt.Sprintf("%d selection regions", l) j = addString(j, y, s, fg, bg) } else if r := sel.Get(0); r.Size() == 0 { row, col := v.RowCol(r.A) s := fmt.Sprintf("Line %d, Column %d", row, col) j = addString(j, y, s, fg, bg) } else { ls := v.Lines(r) s := v.Substr(r) if len(ls) < 2 { s := fmt.Sprintf("%d characters selected", len(s)) j = addString(j, y, s, fg, bg) } else { s := fmt.Sprintf("%d lines %d characters selected", len(ls), len(s)) j = addString(j, y, s, fg, bg) } } if t.status_message != "" { s := fmt.Sprintf("; %s", t.status_message) addString(j, y, s, fg, bg) } }
func (f *frontend) VisibleRegion(bv *backend.View) Region { // TODO return Region{0, bv.Size()} }
func newView(bv *backend.View) *view { v := &view{ id: int(bv.Id()), bv: bv, Title: bv.FileName(), } if len(v.Title) == 0 { v.Title = "untitled" } bv.AddObserver(v) bv.Settings().AddOnChange("qml.view.syntax", v.onChange) bv.Settings().AddOnChange("qml.view.syntaxfile", func(name string) { if name != "syntax" { return } syn := bv.Settings().String("syntax", "Plain Text") syntax := backend.GetEditor().GetSyntax(syn) w := fe.windows[bv.Window()] w.qw.Call("setSyntaxStatus", syntax.Name()) }) bv.Settings().AddOnChange("qml.view.tabSize", func(name string) { if name != "tab_size" { return } ts := bv.Settings().Int("tab_size", 4) w := fe.windows[bv.Window()] w.qw.Call("setIndentStatus", strconv.Itoa(ts)) }) return v }
func (t *tbfe) renderView(v *backend.View, lay layout) { p := util.Prof.Enter("render") defer p.Exit() sx, sy, w, h := lay.x, lay.y, lay.width, lay.height vr := lay.visible runes := v.Substr(vr) x, y := sx, sy ex, ey := sx+w, sy+h style, _ := v.Settings().Get("caret_style", "underline").(string) inverse, _ := v.Settings().Get("inverse_caret_state", false).(bool) caretStyle := getCaretStyle(style, inverse) oldCaretStyle := caretStyle caretBlink, _ := v.Settings().Get("caret_blink", true).(bool) if caretBlink && blink { caretStyle = 0 } tabSize := 4 if i, ok := v.Settings().Get("tab_size", tabSize).(int); ok { tabSize = i } lineNumbers, _ := v.Settings().Get("line_numbers", true).(bool) recipe := v.Transform(vr).Transcribe() fg, bg := defaultFg, defaultBg sel := v.Sel() line, _ := v.RowCol(vr.Begin()) eofline, _ := v.RowCol(v.Size()) lineNumberRenderSize := len(intToRunes(eofline)) for i, r := range runes { fg, bg = defaultFg, defaultBg if lineNumbers { renderLineNumber(&line, &x, y, lineNumberRenderSize, fg, bg) } curr := 0 o := vr.Begin() + i for curr < len(recipe) && (o >= recipe[curr].Region.Begin()) { if o < recipe[curr].Region.End() { fg = palLut(render.Colour(recipe[curr].Flavour.Foreground)) bg = palLut(render.Colour(recipe[curr].Flavour.Background)) } curr++ } iscursor := sel.Contains(Region{o, o}) if iscursor { fg = fg | caretStyle termbox.SetCell(x, y, ' ', fg, bg) } if r == '\t' { add := (x + 1 + (tabSize - 1)) &^ (tabSize - 1) for ; x < add; x++ { if x < ex { termbox.SetCell(x, y, ' ', fg, bg) } // A long cursor looks weird fg = fg & ^(termbox.AttrUnderline | termbox.AttrReverse) } continue } else if r == '\n' { x = sx if y++; y > ey { break } else if lineNumbers { // This results in additional calls to renderLineNumber. // Maybe just accumulate positions needing line numbers, rendering them // after the loop? renderLineNumber(&line, &x, y, lineNumberRenderSize, defaultFg, defaultBg) } continue } if x < ex { termbox.SetCell(x, y, r, fg, bg) } x++ } fg, bg = defaultFg, defaultBg // Need this if the cursor is at the end of the buffer o := vr.Begin() + len(runes) iscursor := sel.Contains(Region{o, o}) if iscursor { fg = fg | caretStyle termbox.SetCell(x, y, ' ', fg, bg) } // restore original caretStyle before blink modification caretStyle = oldCaretStyle if rs := sel.Regions(); len(rs) > 0 { if r := rs[len(rs)-1]; !vr.Covers(r) { t.Show(v, r) } } fg, bg = defaultFg, palLut(render.Colour{28, 29, 26, 1}) y = t.window_layout.height - statusbarHeight // Draw status bar bottom of window for i := 0; i < t.window_layout.width; i++ { termbox.SetCell(i, y, ' ', fg, bg) } go t.renderLStatus(v, y, fg, bg) // The right status rns := []rune(fmt.Sprintf("Tab Size:%d %s", tabSize, "Go")) x = t.window_layout.width - 1 - len(rns) addRunes(x, y, rns, fg, bg) }