├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── example └── main.go ├── giohyperlink.go ├── giohyperlink_android.go ├── giohyperlink_android.jar ├── giohyperlink_android.java ├── giohyperlink_darwin.go ├── giohyperlink_ios.go ├── giohyperlink_js.go ├── giohyperlink_unix.go ├── giohyperlink_windows.go ├── go.mod └── go.sum /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual-licensed under the UNLICENSE or 2 | the MIT license. 3 | 4 | SPDX-License-Identifier: Unlicense OR MIT 5 | 6 | You may use the project under the terms of either license. 7 | 8 | ---- 9 | The MIT License (MIT) 10 | 11 | Copyright (c) 2020 Inkeliz 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | --- 31 | 32 | --- 33 | The UNLICENSE 34 | 35 | This is free and unencumbered software released into the public domain. 36 | 37 | Anyone is free to copy, modify, publish, use, compile, sell, or 38 | distribute this software, either in source code form or as a compiled 39 | binary, for any purpose, commercial or non-commercial, and by any 40 | means. 41 | 42 | In jurisdictions that recognize copyright laws, the author or authors 43 | of this software dedicate any and all copyright interest in the 44 | software to the public domain. We make this dedication for the benefit 45 | of the public at large and to the detriment of our heirs and 46 | successors. We intend this dedication to be an overt act of 47 | relinquishment in perpetuity of all present and future rights to this 48 | software under copyright law. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 51 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 52 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 53 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 54 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 55 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 56 | OTHER DEALINGS IN THE SOFTWARE. 57 | 58 | For more information, please refer to 59 | --- 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GioHyperlink 2 | -------- 3 | 4 | Opens a hyperlink in the default browser. 🤩 5 | 6 | ## Setup 7 | 8 | First, you need to `go get github.com/inkeliz/giohyperlink`, then you need to provide `giohyperlink` access to the Window events, so you need to add the following to your main loop function: 9 | 10 | ```rust 11 | 12 | ```diff 13 | for evt := range w.Events() { // Gio main event loop 14 | + giohyperlink.ListenEvents(e) 15 | 16 | switch evt := evt.(type) { 17 | // ... 18 | } 19 | } 20 | ``` 21 | 22 | > _⚠️In some OSes (Windows, macOS...) this setup is optional, but it's recommended to do it anyway._ 23 | 24 | ## Usage 25 | 26 | To open one link, you can use the `Open` function: 27 | 28 | ```go 29 | giohyperlink.Open("https://github.com") 30 | ``` 31 | 32 | That will open the link in the default browser. You can use `OpenURL` to open a `*url.URL`: 33 | 34 | ```go 35 | giohyperlink.OpenURL(&url.URL{ 36 | Scheme: "https", 37 | Host: "github.com", 38 | }) 39 | ``` 40 | 41 | By default only HTTP and HTTPS links are allowed, but you can change that by changing `InsecureIgnoreScheme` to `true`, 42 | you should validate the URL and scheme on your own. 43 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gioui.org/app" 5 | "gioui.org/f32" 6 | "gioui.org/font/gofont" 7 | "gioui.org/io/system" 8 | "gioui.org/layout" 9 | "gioui.org/op" 10 | "gioui.org/op/clip" 11 | "gioui.org/op/paint" 12 | "gioui.org/text" 13 | "gioui.org/unit" 14 | "gioui.org/widget" 15 | "gioui.org/widget/material" 16 | "github.com/inkeliz/giohyperlink" 17 | "image/color" 18 | "log" 19 | "os" 20 | "strings" 21 | ) 22 | 23 | func main() { 24 | go func() { 25 | w := app.NewWindow(app.Size(unit.Dp(800), unit.Dp(700)), app.MinSize(unit.Dp(400), unit.Dp(400))) 26 | if err := loop(w); err != nil { 27 | panic(err) 28 | } 29 | os.Exit(0) 30 | }() 31 | 32 | app.Main() 33 | } 34 | 35 | func loop(w *app.Window) error { 36 | defer w.Close() 37 | var ops op.Ops 38 | for { 39 | select { 40 | case e := <-w.Events(): 41 | 42 | // You need to include ListenEvents() 43 | giohyperlink.ListenEvents(e) 44 | ////////////////////////////////////// 45 | 46 | switch e := e.(type) { 47 | case system.DestroyEvent: 48 | return e.Err 49 | case system.FrameEvent: 50 | gtx := layout.NewContext(&ops, e) 51 | 52 | for _, ee := range InputAction.Events() { 53 | if _, ok := ee.(widget.SubmitEvent); !ok { 54 | continue 55 | } 56 | 57 | if err := giohyperlink.Open(InputAction.Text()); err != nil { 58 | log.Println(err) 59 | } 60 | } 61 | 62 | if ButtonAction.Clicked() { 63 | if err := giohyperlink.Open(InputAction.Text()); err != nil { 64 | log.Println(err) 65 | } 66 | } 67 | 68 | render(gtx) 69 | e.Frame(gtx.Ops) 70 | } 71 | } 72 | } 73 | } 74 | 75 | func render(gtx layout.Context) layout.Dimensions { 76 | return layout.Flex{Axis: layout.Vertical, Spacing: layout.SpaceSides}.Layout(gtx, 77 | 78 | layout.Rigid(func(gtx layout.Context) layout.Dimensions { 79 | return MarginDesign.Layout(gtx, func(gtx layout.Context) layout.Dimensions { 80 | return InputBackgroundDesign.Layout(gtx, func(gtx layout.Context) layout.Dimensions { 81 | return InputDesign.Layout(gtx, InputAction, "Type some webite (e.g https://gioui.org)", "https://gioui.org") 82 | }) 83 | }) 84 | }), 85 | 86 | layout.Rigid(func(gtx layout.Context) layout.Dimensions { 87 | 88 | return MarginDesign.Layout(gtx, func(gtx layout.Context) layout.Dimensions { 89 | return ButtonDesign.Layout(gtx, ButtonAction, "OPEN") 90 | }) 91 | }), 92 | 93 | ) 94 | } 95 | 96 | // Actions 97 | var ( 98 | ButtonAction = &widget.Clickable{} 99 | InputAction = &widget.Editor{SingleLine: true, Submit: true} 100 | ) 101 | 102 | // Design 103 | var ( 104 | ButtonDesign = &Button{Color: color.NRGBA{R: 255, G: 255, B: 255, A: 255}, TextSize: unit.Sp(16).Scale(14.0 / 16.0), Background: color.NRGBA{R: 135, G: 156, B: 251, A: 255}, BorderRadius: unit.Dp(4), Modifier: strings.ToUpper, Inset: layout.Inset{Top: unit.Dp(10), Right: unit.Dp(12), Bottom: unit.Dp(10), Left: unit.Dp(12)}} 105 | InputDesign = &Input{Font: text.Font{}, TextSize: unit.Dp(14), Color: color.NRGBA{R: 100, G: 130, B: 60, A: 255}, HintColor: color.NRGBA{R: 120, G: 120, B: 120, A: 255}} 106 | InputBackgroundDesign = &Background{Color: color.NRGBA{R: 234, G: 236, B: 231, A: 255}, Inset: layout.UniformInset(unit.Dp(13)), BorderRadius: unit.Dp(10)} 107 | 108 | MarginDesign = layout.Inset{Right: unit.Dp(30), Bottom: unit.Dp(6), Left: unit.Dp(30), Top: unit.Dp(6)} 109 | ) 110 | 111 | var defaultMaterial = material.NewTheme(gofont.Collection()) 112 | 113 | type Input struct { 114 | Font text.Font 115 | TextSize unit.Value 116 | Color color.NRGBA 117 | HintColor color.NRGBA 118 | } 119 | 120 | var alreadySetEditor = make(map[*widget.Editor]bool) 121 | 122 | func (i *Input) Layout(gtx layout.Context, editor *widget.Editor, hint string, value string) layout.Dimensions { 123 | e := material.Editor(defaultMaterial, editor, hint) 124 | e.TextSize = i.TextSize 125 | e.Color = i.Color 126 | e.Hint = hint 127 | e.HintColor = i.HintColor 128 | 129 | if value != "" { 130 | if _, ok := alreadySetEditor[editor]; !ok { 131 | editor.SetText(value) 132 | editor.MoveCaret(editor.Len(), editor.Len()) 133 | alreadySetEditor[editor] = true 134 | } 135 | } 136 | 137 | return e.Layout(gtx) 138 | } 139 | 140 | type Button struct { 141 | Color color.NRGBA 142 | Font text.Font 143 | TextSize unit.Value 144 | Background color.NRGBA 145 | BorderRadius unit.Value 146 | Modifier func(string) string 147 | Inset layout.Inset 148 | } 149 | 150 | func (b *Button) Layout(gtx layout.Context, clickable *widget.Clickable, s string) layout.Dimensions { 151 | style := material.Button(defaultMaterial, clickable, s) 152 | style.Color = b.Color 153 | style.Font = b.Font 154 | style.TextSize = b.TextSize 155 | style.Background = b.Background 156 | style.CornerRadius = b.BorderRadius 157 | style.Inset = b.Inset 158 | 159 | if b.Modifier != nil { 160 | style.Text = b.Modifier(s) 161 | } 162 | 163 | return style.Layout(gtx) 164 | } 165 | 166 | type Background struct { 167 | Color color.NRGBA 168 | BorderRadius unit.Value 169 | Inset layout.Inset 170 | } 171 | 172 | func (b *Background) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions { 173 | 174 | macro := op.Record(gtx.Ops) 175 | dimensions := b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { 176 | return w(gtx) 177 | }) 178 | saved := macro.Stop() 179 | 180 | return layout.Stack{}.Layout(gtx, 181 | layout.Stacked(func(gtx layout.Context) layout.Dimensions { 182 | background := f32.Rectangle{Max: f32.Point{X: float32(dimensions.Size.X), Y: float32(dimensions.Size.Y)}} 183 | 184 | rr := float32(gtx.Px(b.BorderRadius)) 185 | stack := clip.RRect{Rect: background, NE: rr, NW: rr, SE: rr, SW: rr}.Op(gtx.Ops).Push(gtx.Ops) 186 | paint.Fill(gtx.Ops, b.Color) 187 | stack.Pop() 188 | 189 | return dimensions 190 | }), 191 | 192 | layout.Expanded(func(gtx layout.Context) layout.Dimensions { 193 | saved.Add(gtx.Ops) 194 | return dimensions 195 | }), 196 | ) 197 | } 198 | -------------------------------------------------------------------------------- /giohyperlink.go: -------------------------------------------------------------------------------- 1 | package giohyperlink 2 | 3 | import ( 4 | "errors" 5 | "net/url" 6 | 7 | "gioui.org/io/event" 8 | ) 9 | 10 | var ( 11 | // ErrNotReady may occur when try to open a URL before the initialization is done. 12 | ErrNotReady = errors.New("some needed library was not loaded yet, make use that you are using ListenEvents()") 13 | // ErrInvalidURL occur when provide an invalid URL, like a non http/https URL. 14 | ErrInvalidURL = errors.New("given url is invalid") 15 | ) 16 | 17 | var ( 18 | // InsecureIgnoreScheme will remove any attempt to validate the URL 19 | // It's "false" by default. Set it to "true" if you are using a custom scheme (like "myapp://"). 20 | InsecureIgnoreScheme bool 21 | ) 22 | 23 | // ListenEvents must get all the events from Gio, in order to get the GioView once it's ready. You need 24 | // to include that function where you listen for Gio events. 25 | // 26 | // Similar as: 27 | // 28 | // select { 29 | // case e := <-w.Events(): 30 | // giohyperlink.ListenEvents(e) 31 | // 32 | // switch e := e.(type) { 33 | // (( ... your code ... )) 34 | func ListenEvents(event event.Event) { 35 | listenEvents(event) 36 | } 37 | 38 | // OpenURL opens the given url.URL in the browser (or equivalent app) 39 | func OpenURL(u *url.URL) error { 40 | if u == nil || u.Scheme == "" || ((u.Scheme != "http" && u.Scheme != "https") && InsecureIgnoreScheme == false) { 41 | return ErrInvalidURL 42 | } 43 | 44 | return open(u) 45 | } 46 | 47 | // Open opens the given string url in the browser (or equivalent app). 48 | func Open(uri string) error { 49 | if uri == "" { 50 | return ErrInvalidURL 51 | } 52 | 53 | u, err := url.Parse(uri) 54 | if err != nil { 55 | return ErrInvalidURL 56 | } 57 | 58 | return OpenURL(u) 59 | } 60 | -------------------------------------------------------------------------------- /giohyperlink_android.go: -------------------------------------------------------------------------------- 1 | // +build android 2 | 3 | package giohyperlink 4 | 5 | import ( 6 | "gioui.org/app" 7 | "gioui.org/io/event" 8 | "git.wow.st/gmp/jni" 9 | "net/url" 10 | ) 11 | 12 | //go:generate javac -source 8 -target 8 -bootclasspath $ANDROID_HOME\platforms\android-29\android.jar -d $TEMP\giohyperlink\classes giohyperlink_android.java 13 | //go:generate jar cf giohyperlink_android.jar -C $TEMP\giohyperlink\classes . 14 | 15 | var view uintptr 16 | 17 | func listenEvents(event event.Event) { 18 | if e, ok := event.(app.ViewEvent); ok { 19 | view = e.View 20 | } 21 | } 22 | 23 | func open(u *url.URL) error { 24 | if view == 0 { 25 | return ErrNotReady 26 | } 27 | 28 | return jni.Do(jni.JVMFor(app.JavaVM()), func(env jni.Env) error { 29 | 30 | // Get the GioView object 31 | obj := jni.Object(view) 32 | cls := jni.GetObjectClass(env, obj) 33 | 34 | // Run getClass() to get the Class of GioView 35 | mid := jni.GetMethodID(env, cls, "getClass", "()Ljava/lang/Class;") 36 | obj, err := jni.CallObjectMethod(env, obj, mid) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | // Run getClassLoader() to get the ClassLoader from Class 42 | cls = jni.GetObjectClass(env, obj) 43 | mid = jni.GetMethodID(env, cls, "getClassLoader", "()Ljava/lang/ClassLoader;") 44 | obj, err = jni.CallObjectMethod(env, obj, mid) 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | // Run findClass() from ClassLoader. The return is our custom class (in that case it's the 50 | // com.inkeliz.giohyperlink.giohyperlink_android, that name is defined on `giohyperlink_android.java`. 51 | cls = jni.GetObjectClass(env, obj) 52 | mid = jni.GetMethodID(env, cls, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;") 53 | clso, err := jni.CallObjectMethod(env, obj, mid, jni.Value(jni.JavaString(env, `com.inkeliz.giohyperlink.giohyperlink_android`))) 54 | if err != nil { 55 | panic(err) 56 | } 57 | 58 | // We need to convert Object (because we use CallObjectMethod) to jni.Class type 59 | cls = jni.Class(clso) 60 | 61 | // Create a new Object from our class. It's almost the same of `new giohyperlink_android()` 62 | // The `` and `NewObject` are used to create a "variable" with the class that we get before. 63 | mid = jni.GetMethodID(env, cls, "", `()V`) 64 | obj, err = jni.NewObject(env, cls, mid) 65 | if err != nil { 66 | panic(err) 67 | } 68 | 69 | // Run the `open()` function from our custom Java. That is defined inside the `giohyperlink_android` class 70 | // you can view that at giohyperlink_android.java. 71 | // 72 | // Our java function is: 73 | // public void open(View view, String url) {} 74 | // 75 | // So we need to supply the view argument and the url argument. That view argument is the `GioView` itself 76 | // the GioView is the `view` variable, which we got from app.ViewEvents. 77 | mid = jni.GetMethodID(env, cls, "open", "(Landroid/view/View;Ljava/lang/String;)V") 78 | err = jni.CallVoidMethod(env, obj, mid, jni.Value(view), jni.Value(jni.JavaString(env, u.String()))) 79 | if err != nil { 80 | panic(err) 81 | } 82 | 83 | return nil 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /giohyperlink_android.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inkeliz/giohyperlink/2ac5d54abdce8544be0cd008e9ee343b930e44b0/giohyperlink_android.jar -------------------------------------------------------------------------------- /giohyperlink_android.java: -------------------------------------------------------------------------------- 1 | package com.inkeliz.giohyperlink_android; 2 | 3 | import android.app.Activity; 4 | import android.view.View; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | 9 | public class giohyperlink_android { 10 | 11 | public void open(View view, String url) { 12 | 13 | // Create the Intent, which is to open the given URL 14 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 15 | 16 | // Get GioActivity from GioView 17 | Activity activity = (Activity)view.getContext(); 18 | 19 | // Run on main thread, from GioActivity 20 | activity.runOnUiThread(new Runnable() { 21 | public void run() { 22 | 23 | // Create the activity from GioActivity 24 | activity.startActivity(intent); 25 | } 26 | }); 27 | 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /giohyperlink_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin,!ios 2 | 3 | package giohyperlink 4 | 5 | import ( 6 | "gioui.org/io/event" 7 | "net/url" 8 | "os/exec" 9 | ) 10 | 11 | func listenEvents(_ event.Event) { 12 | // NO-OP 13 | } 14 | 15 | func open(u *url.URL) error { 16 | return exec.Command("open", u.String()).Run() 17 | } 18 | -------------------------------------------------------------------------------- /giohyperlink_ios.go: -------------------------------------------------------------------------------- 1 | //go:build ios 2 | // +build ios 3 | 4 | package giohyperlink 5 | 6 | /* 7 | #cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc 8 | 9 | @import UIKit; 10 | 11 | void openLink(char *u) { 12 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString: @(u)] options:@{} completionHandler:nil]; 13 | } 14 | */ 15 | import "C" 16 | 17 | import ( 18 | "gioui.org/io/event" 19 | "net/url" 20 | "unsafe" 21 | ) 22 | 23 | func listenEvents(_ event.Event) { 24 | // NO-OP 25 | } 26 | 27 | func open(u *url.URL) error { 28 | u.RawQuery = u.Query().Encode() 29 | cURL := C.CString(u.String()) 30 | C.openLink(cURL) 31 | C.free(unsafe.Pointer(cURL)) 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /giohyperlink_js.go: -------------------------------------------------------------------------------- 1 | // +build js 2 | 3 | package giohyperlink 4 | 5 | import ( 6 | "gioui.org/io/event" 7 | "gioui.org/io/system" 8 | "net/url" 9 | "syscall/js" 10 | ) 11 | 12 | var ( 13 | _document = js.Global().Get("document") 14 | _body = js.Global().Get("document").Get("body") 15 | ) 16 | 17 | func listenEvents(event event.Event) { 18 | if _, ok := event.(system.StageEvent); ok { 19 | links := _body.Call("querySelectorAll", "a.giohyperlink") 20 | if !links.Truthy() { 21 | return 22 | } 23 | for i := 0; i < links.Length(); i++ { 24 | _body.Call("removeChild", links.Index(0)) 25 | } 26 | } 27 | } 28 | 29 | func open(u *url.URL) error { 30 | if ok := js.Global().Call("open", u.String(), "_blank", "noreferrer,noopener").Truthy(); !ok { 31 | // If there's a error let's use the hacky way: 32 | // It will create a "fullscreen ", which clicking will 33 | // open the URL. 34 | // Generally, it will need two clicks to open the URL. 35 | 36 | // We can't hook into `a` (adding `a.addEvenetListener("click")` will make it fail again, 37 | // not sure why. 38 | // We remove this `a` when the app lost focus (based on Page Visibility API, which Gio relies on). 39 | a := _document.Call("createElement", "a") 40 | a.Set("href", u.String()) 41 | a.Set("target", "_blank") 42 | a.Set("rel", "noreferrer,noopener") 43 | a.Set("innerText", " ") 44 | a.Get("classList").Call("add", "giohyperlink") 45 | a.Get("style").Set("display", "block") 46 | a.Get("style").Set("width", "100vw") 47 | a.Get("style").Set("height", "100vh") 48 | a.Get("style").Set("position", "fixed") 49 | a.Get("style").Set("top", "0") 50 | a.Get("style").Set("z-index", "100") 51 | _body.Call("appendChild", a) 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /giohyperlink_unix.go: -------------------------------------------------------------------------------- 1 | // +build linux,!android openbsd freebsd netbsd dragonfly 2 | 3 | package giohyperlink 4 | 5 | import ( 6 | "gioui.org/io/event" 7 | "net/url" 8 | "os/exec" 9 | ) 10 | 11 | func listenEvents(_ event.Event) { 12 | // NO-OP 13 | } 14 | 15 | func open(u *url.URL) error { 16 | return exec.Command("xdg-open", u.String()).Run() 17 | } 18 | -------------------------------------------------------------------------------- /giohyperlink_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package giohyperlink 4 | 5 | import ( 6 | "gioui.org/io/event" 7 | "golang.org/x/sys/windows" 8 | "net/url" 9 | ) 10 | 11 | func listenEvents(_ event.Event) { 12 | // NO-OP 13 | } 14 | 15 | func open(u *url.URL) error { 16 | return windows.ShellExecute(0, nil, windows.StringToUTF16Ptr(u.String()), nil, nil, windows.SW_SHOWNORMAL) 17 | } 18 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/inkeliz/giohyperlink 2 | 3 | go 1.18 4 | 5 | require ( 6 | gioui.org v0.0.0-20220830130127-276b7eefdd65 7 | git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0 8 | golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 9 | ) 10 | 11 | require ( 12 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 // indirect 13 | gioui.org/shader v1.0.6 // indirect 14 | github.com/benoitkugler/textlayout v0.1.3 // indirect 15 | github.com/gioui/uax v0.2.1-0.20220819135011-cda973fac06d // indirect 16 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b // indirect 17 | golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 // indirect 18 | golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect 19 | golang.org/x/text v0.3.7 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | eliasnaur.com/font v0.0.0-20220124212145-832bb8fc08c3 h1:djFprmHZgrSepsHAIRMp5UJn3PzsoTg9drI+BDmif5Q= 2 | gioui.org v0.0.0-20220830130127-276b7eefdd65 h1:mX+A86TwTyHZNqDxekUukiAmtYNUOq4CnrRZHxUrlo8= 3 | gioui.org v0.0.0-20220830130127-276b7eefdd65/go.mod h1:GN091SCcGAfHfQiSOetXx7Abdy+8nmONj0ZN63Xxf7w= 4 | gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= 5 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc= 6 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= 7 | gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y= 8 | gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= 9 | git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0 h1:Ynp3h+TC8k1clvf45D28VFQlmy0bPx8M/MG5bB24Vj8= 10 | git.wow.st/gmp/jni v0.0.0-20200827154156-014cd5c7c4c0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo= 11 | github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= 12 | github.com/benoitkugler/textlayout v0.0.5/go.mod h1:puH4v13Uz7uIhIH0XMk5jgc8U3MXcn5r3VlV9K8n0D8= 13 | github.com/benoitkugler/textlayout v0.1.3 h1:Jv0E28xDkke3KrWle90yOLtBmZsUqXLBy70lZRfbKN0= 14 | github.com/benoitkugler/textlayout v0.1.3/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w= 15 | github.com/benoitkugler/textlayout-testdata v0.1.1 h1:AvFxBxpfrQd8v55qH59mZOJOQjtD6K2SFe9/HvnIbJk= 16 | github.com/gioui/uax v0.2.1-0.20220819135011-cda973fac06d h1:ro1W5kY1pVBLHy4GokZUfr9cl7ewZhAiT5WsXqFDYE4= 17 | github.com/gioui/uax v0.2.1-0.20220819135011-cda973fac06d/go.mod h1:b6uGh9ySJPVQG/RdiI88bE5sUGDk6vzzRujv1BAeuJc= 18 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b h1:WINlj3ANt+CVrO2B4NGDHRlPvEWZPxjhb7z+JKypwXI= 19 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b/go.mod h1:ZNYu5saGoMOqtkVH5T8onTwhzenDUVszI+5WFHJRaxQ= 20 | golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0= 21 | golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8= 22 | golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 23 | golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 24 | golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU= 25 | golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= 26 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 27 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 28 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 29 | golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= 30 | golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 31 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 32 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 33 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 34 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 35 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 36 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 37 | --------------------------------------------------------------------------------