func (f *frontend) Prompt(title, folder string, flags int) []string { w := f.windows[backend.GetEditor().ActiveWindow()] obj := w.qw.ObjectByName("fileDialog") obj.Set("title", title) obj.Set("folder", "file://"+folder) obj.Set("selectExisting", flags&backend.PROMPT_SAVE_AS == 0) obj.Set("selectFolder", flags&backend.PROMPT_ONLY_FOLDER == 1) obj.Set("selectMultiple", flags&backend.PROMPT_SELECT_MULTIPLE == 1) f.promptWaitGroup.Add(1) obj.Call("open") f.promptWaitGroup.Wait() if f.promptResult != "accepted" { return nil } res := obj.List("fileUrls") files := make([]string, res.Len()) res.Convert(&files) for i, file := range files { if file[:7] == "file://" { files[i] = file[7:] } } log.Fine("Selected %s files", files) return files }
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 (f *frontend) StatusMessage(msg string) { w := f.windows[backend.GetEditor().ActiveWindow()] w.qw.Call("setFrontendStatus", msg) go func() { time.Sleep(5 * time.Second) w.qw.Call("setFrontendStatus", "") }() }
func (t *tbfe) setupEditor() *backend.Editor { ed := backend.GetEditor() ed.SetFrontend(t) ed.LogInput(false) ed.LogCommands(false) return ed }
func TestUpdateVisibleRegion(t *testing.T) { var ( fe tbfe e = backend.GetEditor() w = e.NewWindow() v = w.NewFile() ) fe.layout = make(map[*backend.View]layout) fe.layout[v] = layout{0, 0, 100, 100 - *consoleHeight - 1, Region{}, 0} fe.setupCallbacks(v) edit := v.BeginEdit() v.Insert(edit, 0, "foo") v.EndEdit(edit) if end := fe.layout[v].visible.End(); end != 3 { t.Fatalf("Expected 3, got %d", end) } }
func (f *frontend) message(text string, icon, btns int) string { cbs := make(map[string]int) if btns&okButton != 0 { cbs["accepted"] = 1 } if btns&cancelButton != 0 { cbs["rejected"] = 0 } w := f.windows[backend.GetEditor().ActiveWindow()] obj := w.qw.ObjectByName("messageDialog") obj.Set("text", text) obj.Set("icon", icon) obj.Set("standardButtons", btns) f.promptWaitGroup.Add(1) obj.Call("open") f.promptWaitGroup.Wait() log.Fine("returning %d from dialog", f.promptResult) return f.promptResult }
func (f *frontend) HandleInput(text string, keycode int, modifiers int) bool { log.Debug("frontend.HandleInput: text=%v, key=%x, modifiers=%x", text, keycode, modifiers) shift := false alt := false ctrl := false super := false if key, ok := lut[keycode]; ok { ed := backend.GetEditor() if modifiers&shift_mod != 0 { shift = true } if modifiers&alt_mod != 0 { alt = true } if modifiers&ctrl_mod != 0 { if runtime.GOOS == "darwin" { super = true } else { ctrl = true } } if modifiers&meta_mod != 0 { if runtime.GOOS == "darwin" { ctrl = true } else { super = true } } ed.HandleInput(keys.KeyPress{Text: text, Key: key, Shift: shift, Alt: alt, Ctrl: ctrl, Super: super}) return true } return false }
func (f *frontend) loop() (err error) { ed := backend.GetEditor() // TODO: As InitCallback doc says initiation code to be deferred until // after the UI is up and running. but because we dont have any // scheme we are initing editor before the UI comes up. ed.Init() ed.SetDefaultPath("../packages/Default") ed.SetUserPath("../packages/User") ed.SetClipboardFuncs(clipboard.WriteAll, clipboard.ReadAll) // Some packages(e.g Vintageos) need available window and view at start // so we need at least one window and view before loading packages. // Sublime text also has available window view on startup w := ed.NewWindow() w.NewFile() ed.AddPackagesPath("../packages") ed.SetFrontend(f) ed.LogInput(false) ed.LogCommands(false) c := ed.Console() f.Console = newView(c) c.AddObserver(f.Console) c.AddObserver(f) var ( engine *qml.Engine component qml.Object // WaitGroup keeping track of open windows wg sync.WaitGroup ) // create and setup a new engine, destroying // the old one if one exists. // // This is needed to re-load qml files to get // the new file contents from disc as otherwise // the old file would still be what is referenced. newEngine := func() (err error) { if engine != nil { log.Debug("calling destroy") // TODO(.): calling this appears to make the editor *very* crash-prone, just let it leak for now // engine.Destroy() engine = nil } log.Debug("calling newEngine") engine = qml.NewEngine() engine.On("quit", f.Quit) log.Fine("setvar frontend") engine.Context().SetVar("frontend", f) log.Fine("loading %s", qmlWindowFile) component, err = engine.LoadFile(qmlWindowFile) return } if err := newEngine(); err != nil { log.Error("Error on creating new engine: %s", err) panic(err) } addWindow := func(bw *backend.Window) { w := newWindow(bw) f.windows[bw] = w w.launch(&wg, component) } backend.OnNew.Add(f.onNew) backend.OnClose.Add(f.onClose) backend.OnLoad.Add(f.onLoad) backend.OnSelectionModified.Add(f.onSelectionModified) backend.OnNewWindow.Add(addWindow) backend.OnStatusChanged.Add(f.onStatusChanged) // we need to add windows and views that are added before we registered // actions for OnNewWindow and OnNew events for _, w := range ed.Windows() { addWindow(w) for _, v := range w.Views() { f.onNew(v) f.onLoad(v) } } defer func() { fmt.Println(util.Prof) }() // The rest of code is related to livereloading qml files // TODO: this doesnt work currently watch, err := fsnotify.NewWatcher() if err != nil { log.Error("Unable to create file watcher: %s", err) return } defer watch.Close() watch.Add("qml") defer watch.Remove("qml") reloadRequested := false waiting := false go func() { // reloadRequested = true // f.Quit() lastTime := time.Now() for { select { case ev := <-watch.Events: if time.Now().Sub(lastTime) < 1*time.Second { // quitting too frequently causes crashes lastTime = time.Now() continue } if strings.HasSuffix(ev.Name, ".qml") && ev.Op == fsnotify.Write && ev.Op != fsnotify.Chmod && !reloadRequested && waiting { reloadRequested = true f.Quit() lastTime = time.Now() } } } }() for { // Reset reload status reloadRequested = false log.Debug("Waiting for all windows to close") // wg would be the WaitGroup all windows belong to, so first we wait for // all windows to close. waiting = true wg.Wait() waiting = false log.Debug("All windows closed. reloadRequest: %v", reloadRequested) // then we check if there's a reload request in the pipe if !reloadRequested || len(f.windows) == 0 { // This would be a genuine exit; all windows closed by the user break } // *We* closed all windows because we want to reload freshly changed qml // files. for { log.Debug("Calling newEngine") if err := newEngine(); err != nil { // Reset reload status reloadRequested = false waiting = true log.Error(err) for !reloadRequested { // This loop allows us to re-try reloading // if there was an error in the file this time, // we just loop around again when we receive the next // reload request (ie on the next save of the file). time.Sleep(time.Second) } waiting = false continue } log.Debug("break") break } log.Debug("re-launching all windows") // Succeeded loading the file, re-launch all windows for _, w := range f.windows { w.launch(&wg, component) for _, bv := range w.Back().Views() { f.onNew(bv) f.onLoad(bv) } } } return }
func (f *frontend) colorScheme() backend.ColorScheme { ed := backend.GetEditor() return ed.GetColorScheme(ed.Settings().String("color_scheme", "")) }
func (f *frontend) RunCommandWithArgs(command string, args backend.Args) { ed := backend.GetEditor() go ed.RunCommand(command, args) }
func (f *frontend) scroll(b Buffer) { f.Show(backend.GetEditor().Console(), Region{b.Size(), b.Size()}) }
func (t *tbfe) scroll(b Buffer) { t.Show(backend.GetEditor().Console(), Region{b.Size(), b.Size()}) }