func TestWebView_Title(t *testing.T) { webView := NewWebView() defer webView.Destroy() wantTitle := "foo" var gotTitle string webView.Connect("notify::title", func() { glib.IdleAdd(func() bool { gotTitle = webView.Title() if gotTitle != "" { gtk.MainQuit() } return false }) }) glib.IdleAdd(func() bool { webView.LoadHTML("<html><head><title>"+wantTitle+"</title></head><body></body></html>", "") return false }) gtk.Main() if wantTitle != gotTitle { t.Errorf("want title %q, got %q", wantTitle, gotTitle) } }
func TestWebView_URI(t *testing.T) { setup() defer teardown() mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {}) wantURI := server.URL + "/" var gotURI string webView.Connect("notify::uri", func() { glib.IdleAdd(func() bool { gotURI = webView.URI() if gotURI != "" { gtk.MainQuit() } return false }) }) glib.IdleAdd(func() bool { webView.LoadURI(server.URL) return false }) gtk.Main() if wantURI != gotURI { t.Errorf("want URI %q, got %q", wantURI, gotURI) } }
func (app *Applet) createGui(init, show bool) { if app.gui != nil { glib.IdleAdd(func() { app.Window().SetVisibility(show) }) return } glib.IdleAdd(func() { app.gui, app.win = guigtk.NewGui(app.cp) if app.gui == nil { return } app.gui.Load() app.win.SetIconFromFile(app.FileLocation("icon")) // TODO: debug path.Join(localDir, "data/icon.png") app.win.Connect("delete-event", func() bool { app.gui, app.win = nil, nil; return false }) // app.win.Connect("delete-event", func() bool { window.Iconify(); return true }) if init { app.cpInit() } if !show { app.win.Iconify() } }) }
// EvaluateJavaScript runs the JavaScript in script in the view's context and // returns the script's result as a Go value. func (v *View) EvaluateJavaScript(script string) (result interface{}, err error) { resultChan := make(chan interface{}, 1) errChan := make(chan error, 1) glib.IdleAdd(func() bool { v.WebView.RunJavaScript(script, func(result *gojs.Value, err error) { glib.IdleAdd(func() bool { if err == nil { goval, err := result.GoValue() if err != nil { errChan <- err return false } resultChan <- goval } else { errChan <- err } return false }) }) return false }) select { case result = <-resultChan: return result, nil case err = <-errChan: return nil, err } }
func main() { gtk.Init(nil) win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL) if err != nil { log.Fatal("Unable to create window:", err) } win.Connect("destroy", func() { gtk.MainQuit() }) win.Add(windowWidget()) // Native GTK is not thread safe, and thus, gotk3's GTK bindings may not // be used from other goroutines. Instead, glib.IdleAdd() must be used // to add a function to run in the GTK main loop when it is in an idle // state. // // Two examples of using glib.IdleAdd() are shown below. The first runs // a user created function, LabelSetTextIdle, and passes it two // arguments for a label and the text to set it with. The second calls // (*gtk.Label).SetText directly, passing in only the text as an // argument. // // If the function passed to glib.IdleAdd() returns one argument, and // that argument is a bool, this return value will be used in the same // manner as a native g_idle_add() call. If this return value is false, // the function will be removed from executing in the GTK main loop's // idle state. If the return value is true, the function will continue // to execute when the GTK main loop is in this state. go func() { for { time.Sleep(time.Second) s := fmt.Sprintf("Set a label %d time(s)!", nSets) _, err := glib.IdleAdd(LabelSetTextIdle, topLabel, s) if err != nil { log.Fatal("IdleAdd() failed:", err) } nSets++ s = fmt.Sprintf("Set a label %d time(s)!", nSets) _, err = glib.IdleAdd(bottomLabel.SetText, s) if err != nil { log.Fatal("IdleAdd() failed:", err) } nSets++ } }() win.ShowAll() gtk.Main() }
func TestWebView_RunJavaScript(t *testing.T) { webView := NewWebView() defer webView.Destroy() wantResultString := "abc" webView.Connect("load-changed", func(_ *glib.Object, loadEvent LoadEvent) { switch loadEvent { case LoadFinished: webView.RunJavaScript(`document.getElementById("foo").innerHTML`, func(result *gojs.Value, err error) { if err != nil { t.Errorf("RunJavaScript error: %s", err) } resultString := webView.JavaScriptGlobalContext().ToStringOrDie(result) if wantResultString != resultString { t.Errorf("want result string %q, got %q", wantResultString, resultString) } gtk.MainQuit() }) } }) glib.IdleAdd(func() bool { webView.LoadHTML(`<p id=foo>abc</p>`, "") return false }) gtk.Main() }
func TestWebView_LoadHTML(t *testing.T) { webView := NewWebView() defer webView.Destroy() loadOk := false webView.Connect("load-failed", func() { t.Errorf("load failed") }) webView.Connect("load-changed", func(_ *glib.Object, loadEvent LoadEvent) { switch loadEvent { case LoadFinished: loadOk = true gtk.MainQuit() } }) glib.IdleAdd(func() bool { webView.LoadHTML("<p>hello</p>", "") return false }) gtk.Main() if !loadOk { t.Error("!loadOk") } }
func TestWebView_LoadURI_load_failed(t *testing.T) { webView := NewWebView() defer webView.Destroy() loadFailed := false loadFinished := false webView.Connect("load-failed", func() { loadFailed = true }) webView.Connect("load-changed", func(_ *glib.Object, loadEvent LoadEvent) { switch loadEvent { case LoadFinished: loadFinished = true gtk.MainQuit() } }) glib.IdleAdd(func() bool { // Load a bad URL to trigger load failure. webView.LoadURI("http://127.0.0.1:99999") return false }) gtk.Main() if !loadFailed { t.Error("!loadFailed") } if !loadFinished { t.Error("!loadFinished") } }
// NewView creates a new View in the context. func (c *Context) NewView() *View { view := make(chan *View, 1) glib.IdleAdd(func() bool { webView := webkit2.NewWebView() settings := webView.Settings() settings.SetEnableWriteConsoleMessagesToStdout(true) settings.SetUserAgentWithApplicationDetails("WebLoop", "v1") v := &View{WebView: webView} loadChangedHandler, _ := webView.Connect("load-changed", func(_ *glib.Object, loadEvent webkit2.LoadEvent) { switch loadEvent { case webkit2.LoadFinished: // If we're here, then the load must not have failed, because // otherwise we would've disconnected this handler in the // load-failed signal handler. v.load <- struct{}{} } }) webView.Connect("load-failed", func() { v.lastLoadErr = ErrLoadFailed webView.HandlerDisconnect(loadChangedHandler) }) view <- v return false }) return <-view }
func TestWebView_GetSnapshot(t *testing.T) { webView := NewWebView() defer webView.Destroy() webView.Connect("load-changed", func(_ *glib.Object, loadEvent LoadEvent) { switch loadEvent { case LoadFinished: webView.GetSnapshot(func(img *image.RGBA, err error) { if err != nil { t.Errorf("GetSnapshot error: %q", err) } if img.Pix == nil { t.Error("!img.Pix") } if img.Stride == 0 || img.Rect.Max.X == 0 || img.Rect.Max.Y == 0 { t.Error("!img.Stride or !img.Rect.Max.X or !img.Rect.Max.Y") } gtk.MainQuit() }) } }) glib.IdleAdd(func() bool { webView.LoadHTML(`<p id=foo>abc</p>`, "") return false }) gtk.Main() }
func TestWebView_RunJavaScript_exception(t *testing.T) { webView := NewWebView() defer webView.Destroy() wantErr := errors.New("An exception was raised in JavaScript") webView.Connect("load-changed", func(_ *glib.Object, loadEvent LoadEvent) { switch loadEvent { case LoadFinished: webView.RunJavaScript(`throw new Error("foo")`, func(result *gojs.Value, err error) { if result != nil { ctx := webView.JavaScriptGlobalContext() t.Errorf("want result == nil, got %q", ctx.ToStringOrDie(result)) } if !reflect.DeepEqual(wantErr, err) { t.Errorf("want error %q, got %q", wantErr, err) } gtk.MainQuit() }) } }) glib.IdleAdd(func() bool { webView.LoadHTML(`<p></p>`, "") return false }) gtk.Main() }
// Title returns the title of the current resource in the view. func (v *View) Title() string { title := make(chan string, 1) glib.IdleAdd(func() bool { title <- v.WebView.Title() return false }) return <-title }
// URI returns the URI of the current resource in the view. func (v *View) URI() string { uri := make(chan string, 1) glib.IdleAdd(func() bool { uri <- v.WebView.URI() return false }) return <-uri }
// addIdle adds a function to call on the next gtk idle cycle, to safely use // the dock with our goroutines. // It will also start the callIdle flush if it's not running. // func addIdle(call func()) { idleMu.Lock() idleDraw = append(idleDraw, call) if !idleRun { idleRun = true glib.IdleAdd(callIdle) } idleMu.Unlock() }
func init() { Write = func(text string) error { clip, e := gtk.ClipboardGet(gdk.SELECTION_CLIPBOARD) if e != nil { return e } glib.IdleAdd(func() { clip.SetText(text) }) return nil } Read = func() (string, error) { clip, e := gtk.ClipboardGet(gdk.SELECTION_CLIPBOARD) if e != nil { return "", e } cs := make(chan (string)) ce := make(chan (error)) defer func() { close(cs) close(ce) }() done := false glib.IdleAdd(func() { // Synced in the GTK loop to prevent thread crashs. str, e := clip.WaitForText() if !done { done = true cs <- str ce <- e } }) go func() { <-time.After(time.Second * 3) if !done { done = true cs <- "" ce <- errors.New("clipboard read timeout") } }() return <-cs, <-ce } }
// Open starts loading the resource at the specified URL. func (v *View) Open(url string) { v.load = make(chan struct{}, 1) v.lastLoadErr = nil glib.IdleAdd(func() bool { if !v.destroyed { v.WebView.LoadURI(url) } return false }) }
// Load HTML from given string with given base URI. func (v *View) LoadHTML(content, baseURI string) { v.load = make(chan struct{}, 1) v.lastLoadErr = nil glib.IdleAdd(func() bool { if !v.destroyed { v.WebView.LoadHTML(content, baseURI) } return false }) }
func runOnUIThreadAndWait(task uiTask) interface{} { resultChannel := make(chan interface{}) defer close(resultChannel) glib.IdleAdd(func() bool { resultChannel <- task() return false }) return <-resultChannel }
// Load loads an applet or theme in the preview. Handbooker and Appleter can be used. // func (widget *Preview) Load(pack Previewer) { widget.title.SetMarkup(common.Big(common.Bold(pack.GetTitle()))) author := pack.GetAuthor() if author != "" { author = fmt.Sprintf(tran.Slate("by %s"), author) } widget.author.SetMarkup(common.Small(common.Mono(author))) apl, ok := pack.(Appleter) if ok { widget.stateText.SetMarkup(apl.FormatState()) widget.size.SetMarkup(common.Small(apl.FormatSize())) if icon := apl.IconState(); icon != "" { if pixbuf, e := common.PixbufAtSize(icon, 24, 24); !widget.log.Err(e, "Load image pixbuf") { widget.stateIcon.SetFromPixbuf(pixbuf) widget.stateIcon.Show() } } } // widget.RemoveTmpFile() widget.previewFrame.Hide() // Hide the preview frame until we have an image. // Async calls for description and image. They can have to be downloaded and be slow at it. chDesc := make(chan (string)) go func() { // Go routines to get data. chDesc <- pack.GetDescription() }() go func() { imageLocation := pack.GetPreviewFilePath() // imageLocation, isTemp := pack.GetPreview(widget.TmpFile) // reuse the same tmp location if needed. desc := <-chDesc glib.IdleAdd(func() { // glib Idle to show the result. widget.description.SetMarkup(desc) widget.setImage(imageLocation) }) }() }
func Example() { runtime.LockOSThread() gtk.Init(nil) webView := webkit2.NewWebView() defer webView.Destroy() webView.Connect("load-failed", func() { fmt.Println("Load failed.") }) webView.Connect("load-changed", func(_ *glib.Object, i int) { loadEvent := webkit2.LoadEvent(i) switch loadEvent { case webkit2.LoadFinished: fmt.Println("Load finished.") fmt.Printf("Title: %q\n", webView.Title()) fmt.Printf("URI: %s\n", webView.URI()) webView.RunJavaScript("window.location.hostname", func(val *gojs.Value, err error) { if err != nil { fmt.Println("JavaScript error.") } else { fmt.Printf("Hostname (from JavaScript): %q\n", val) } gtk.MainQuit() }) } }) glib.IdleAdd(func() bool { webView.LoadURI("https://www.google.com/") return false }) gtk.Main() // output: // Load finished. // Title: "Google" // URI: https://www.google.com/ // Hostname (from JavaScript): "www.google.com" }
// TestConnectNotifySignal ensures that property notification signals (those // whose name begins with "notify::") are queried by the name "notify" (with the // "::" and the property name omitted). This is because the signal is "notify" // and the characters after the "::" are not recognized by the signal system. // // See // https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#GObject-notify // for background, and // https://developer.gnome.org/gobject/stable/gobject-Signals.html#g-signal-new // for the specification of valid signal names. func TestConnectNotifySignal(t *testing.T) { runtime.LockOSThread() // Create any GObject that has defined properties. spacing := 0 box, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, spacing) // Connect to a "notify::" signal to listen on property changes. box.Connect("notify::spacing", func() { gtk.MainQuit() }) glib.IdleAdd(func(s string) bool { t.Log(s) spacing++ box.SetSpacing(spacing) return true }, "IdleAdd executed") gtk.Main() }
func main() { gtk.Init(nil) win, e := gtk.WindowNew(gtk.WINDOW_TOPLEVEL) if e != nil { println(e.Error()) return } path, isTest := vdata.TestPathDefault() var saveCall func(cftype.Builder) if isTest { saveCall = cfprint.Updated } else { saveCall = func(build cftype.Builder) { cfprint.Default(build, true) } } source := vdata.New(log.NewLog(log.Logs), win, saveCall) build := vdata.TestInit(source, path) source.SetGrouper(build) glib.IdleAdd(packWindow(win, source, build)) gtk.Main() }
func TestWebView_LoadURI(t *testing.T) { setup() defer teardown() responseOk := false mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { w.Write([]byte("abc")) responseOk = true }) loadFinished := false webView.Connect("load-failed", func() { t.Errorf("load failed") }) webView.Connect("load-changed", func(_ *glib.Object, loadEvent LoadEvent) { switch loadEvent { case LoadFinished: loadFinished = true gtk.MainQuit() } }) glib.IdleAdd(func() bool { webView.LoadURI(server.URL) return false }) gtk.Main() if !responseOk { t.Error("!responseOk") } if !loadFinished { t.Error("!loadFinished") } }
func (*RealGlib) IdleAdd(f interface{}, args ...interface{}) (glibi.SourceHandle, error) { res, err := glib.IdleAdd(f, args...) return glibi.SourceHandle(res), err }
// NewApplet creates a new applet instance. // func NewApplet(base cdtype.AppBase, events *cdtype.Events) cdtype.AppInstance { app := &Applet{AppBase: base} app.SetConfig(&app.conf, app.actions()...) // Events. events.OnClick = func() { // Left click: open and manage the gui window. if app.Window().IsOpened() { // Window opened. app.Window().ToggleVisibility() } else { app.createGui(true, true) } } events.OnMiddleClick = func() { app.Action().Launch(app.Action().ID(app.conf.ActionClickMiddle)) } events.OnScroll = func(scrollUp bool) { var key int switch app.conf.ActionMouseWheel { case "Change volume": key = ternary.Int(scrollUp, int(upnptype.ActionVolumeUp), int(upnptype.ActionVolumeDown)) case "Seek in track": key = ternary.Int(scrollUp, int(upnptype.ActionSeekForward), int(upnptype.ActionSeekBackward)) } app.Action().Launch(key) } events.OnBuildMenu = app.Action().CallbackMenu(dockMenu) events.End = func() { if app.win != nil { glib.IdleAdd(app.win.Destroy) } } // Create the UPnP device manager. var e error app.cp, e = gupnp.New(&logger{app.Log()}) app.Log().Err(e, "temp Dir") // Connect local tests. hook := app.cp.SubscribeHook("applet") hook.OnRendererFound = app.onMediaRendererFound hook.OnServerFound = app.onMediaServerFound hook.OnRendererLost = app.onMediaRendererLost hook.OnServerLost = app.onMediaServerLost // hook.OnRendererSelected = gui.SetRenderer // hook.OnServerSelected = gui.SetServer // hook.OnTransportState = func(r upnptype.Renderer, state upnptype.PlaybackState) { gui.SetPlaybackState(state) } // hook.OnCurrentTrackDuration = func(r upnptype.Renderer, dur int) { gui.SetDuration(mediacp.TimeToString(dur)) } // hook.OnCurrentTrackMetaData = func(r upnptype.Renderer, item upnptype.Item) { gui.SetTitle(item.Title) } // hook.OnMute = func(r upnptype.Renderer, muted bool) { gui.SetMuted(muted) } // hook.OnVolume = func(r upnptype.Renderer, vol uint) { gui.SetVolume(int(vol)) } // hook.OnCurrentTime = func(r upnptype.Renderer, secs int, f float64) { gui.SetCurrentTime(secs, f*100) } // hook.OnSetVolumeDelta = func(delta int) { gui.SetVolumeDelta(delta) } // } // Connect an UPnP backend to the manager. // mgr := backendsonos.NewManager(&logger{app.Log()}) // mgr.SetEvents(app.cp.DefineEvents()) // go mgr.Start(true) cp := backendgupnp.NewControlPoint() cp.SetEvents(app.cp.DefineEvents()) return app }
// ClickedQuit launches the OnQuit event defined. // The OnQuit action is delayed to the next glib iteration to let GTK finish // its current action (like closing a menu before the close window). // func (widget *GuiConfigure) ClickedQuit() { if widget.OnQuit != nil { glib.IdleAdd(widget.OnQuit) } }
// Strings adds a string widget. Many options included. // TODO: need password cypher, G_USER_DIRECTORY_PICTURES, play sound. // func Strings(key *cftype.Key) { value := key.Value().String() widget := newgtk.Entry() widget.SetText(value) if key.IsType(cftype.KeyPasswordEntry) { // Hide text and decrypt value. widget.SetVisibility(false) value = key.Source().DecryptString(value) } // Pack the widget before any other (in full size if needed). // So we do it now and fill the callbacks later key.PackKeyWidget(key, nil, nil, widget) var setValue func(interface{}) // Add special buttons to fill the entry box. switch key.Type { case cftype.KeyFileSelector, cftype.KeyFolderSelector, cftype.KeySoundSelector, cftype.KeyImageSelector: // Add a file selector. fileChooser := newgtk.Button() img := newgtk.ImageFromIconName("document-open", gtk.ICON_SIZE_SMALL_TOOLBAR) fileChooser.SetImage(img) fileChooser.Connect("clicked", onFileChooserOpen, fileChooserData{widget, key}) key.PackSubWidget(fileChooser) if key.IsType(cftype.KeySoundSelector) { //Sound Play Button play := newgtk.Button() imgPlay := newgtk.ImageFromIconName("media-playback-start", gtk.ICON_SIZE_SMALL_TOOLBAR) play.SetImage(imgPlay) // play.Connect("clicked", C._cairo_dock_play_a_sound, data) key.PackSubWidget(play) } case cftype.KeyShortkeySelector, cftype.KeyClassSelector: // Add a key/class grabber. grab := newgtk.ButtonWithLabel("Grab") key.PackSubWidget(grab) // gtk_widget_add_events(pMainWindow, GDK_KEY_PRESS_MASK); switch key.Type { case cftype.KeyClassSelector: grab.Connect("clicked", func() { widget.SetSensitive(false) // Block widgets. grab.SetSensitive(false) go func() { class, e := key.Source().GrabWindowClass() // Wait user selection in a routine. glib.IdleAdd(func() { // And resync with the GTK loop to display results. widget.SetSensitive(true) // Reactivate widgets. grab.SetSensitive(true) if !key.Log().Err(e, "ClassSelector", key.Group, key.Name) { setValue(class) // On success. key.Log().Debug("KeyClassSelector window selected", class) } }) }() }) case cftype.KeyShortkeySelector: grab.Connect("clicked", onKeyGrabClicked, &textGrabData{entry: widget, win: key.Source().GetWindow()}) } // for _, sk := range key.Source().ListShortkeys() { // if sk.GetConfFilePath() == key.Storage().FilePath() { // println("found file shortkey") // } // } } // Display a default value when empty. if len(key.AuthorizedValues) > 0 && key.AuthorizedValues[0] != "" { defaultText := key.Translate(key.AuthorizedValues[0]) cbChanged, _ := widget.Connect("changed", onTextDefaultChanged, key) data := textDefaultData{key: key, text: defaultText, cbID: cbChanged} widget.Connect("focus-in-event", onTextDefaultFocusIn, data) widget.Connect("focus-out-event", onTextDefaultFocusOut, data) // TODO: check other use of those fields. // g_object_set_data (G_OBJECT (pEntry), "ignore-value", GINT_TO_POINTER (TRUE)); // g_object_set_data (G_OBJECT (pOneWidget), "default-text", cDefaultText); context, _ := widget.GetStyleContext() context.AddProvider(MainCSS(), gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) setValue = func(uncast interface{}) { // if !key.IsDefault { // not sure why this was here. widget.SetText(uncast.(string)) onTextDefaultFocusOut(widget, nil, data) } setValue(value) // will set IsDefault and button state. } else { setValue = func(uncast interface{}) { widget.SetText(uncast.(string)) } } getValue := func() (text interface{}) { if key.IsDefault { return "" } text, _ = widget.GetText() return text } key.PackKeyWidget(key, getValue, setValue) }
// LaunchCommand adds a launch command widget. // HELP ONLY // func LaunchCommand(key *cftype.Key) { if len(key.AuthorizedValues) == 0 || key.AuthorizedValues[0] == "" { key.Log().NewErrf("command missing", "widget LaunchCommand: %s", key.Name) return } // log.Info(key.Name, key.AuthorizedValues) if key.IsType(cftype.KeyLaunchCmdIf) { if len(key.AuthorizedValues) < 2 { key.Label().SetSensitive(false) return } key.Log().Info("KeyLaunchCmdIf : disabled for now") return // key.Log().Info("test", key.AuthorizedValues[1]) // key.Log().Err(key.Log().ExecShow(key.AuthorizedValues[1]), "exec test") // gchar *cSecondCommand = pAuthorizedValuesList[1]; // gchar *cResult = cairo_dock_launch_command_sync (cSecondCommand); // cd_debug ("%s: %s => %s", __func__, cSecondCommand, cResult); // if (cResult == NULL || *cResult == '0' || *cResult == '\0') // result is 'fail' // { // gtk_widget_set_sensitive (pLabel, FALSE); // g_free (cResult); // break ; // } // g_free (cResult); } spinner := newgtk.Spinner() spinner.SetNoShowAll(true) key.PackSubWidget(spinner) btn := newgtk.ButtonFromIconName("go-jump", gtk.ICON_SIZE_BUTTON) key.PackSubWidget(btn) btn.Connect("clicked", func() { cmd, e := key.Log().ExecShlex(key.AuthorizedValues[0]) if key.Log().Err(e, "widget LaunchCommand parse command", key.Name, ":", key.AuthorizedValues[0]) { return } e = cmd.Start() if key.Log().Err(e, "widget LaunchCommand exec", key.AuthorizedValues[0]) { return } btn.Hide() spinner.Show() spinner.Start() // Wait the external program in a go routine. // When finished, restore buttons state in the gtk idle loop. go func() { cmd.Wait() glib.IdleAdd(func() { btn.Show() spinner.Hide() spinner.Stop() }) }() }) // G_CALLBACK (_cairo_dock_widget_launch_command), }