123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827 |
- // SPDX-License-Identifier: Unlicense OR MIT
- package app
- import (
- "fmt"
- "image"
- "image/color"
- "io"
- "strings"
- "syscall/js"
- "time"
- "unicode"
- "unicode/utf8"
- "gioui.org/internal/f32color"
- "gioui.org/op"
- "gioui.org/f32"
- "gioui.org/io/event"
- "gioui.org/io/key"
- "gioui.org/io/pointer"
- "gioui.org/io/system"
- "gioui.org/io/transfer"
- "gioui.org/unit"
- )
- type JSViewEvent struct {
- Element js.Value
- }
- type contextStatus int
- const (
- contextStatusOkay contextStatus = iota
- contextStatusLost
- contextStatusRestored
- )
- type window struct {
- window js.Value
- document js.Value
- head js.Value
- clipboard js.Value
- cnv js.Value
- tarea js.Value
- w *callbacks
- redraw js.Func
- clipboardCallback js.Func
- requestAnimationFrame js.Value
- browserHistory js.Value
- visualViewport js.Value
- screenOrientation js.Value
- cleanfuncs []func()
- touches []js.Value
- composing bool
- requestFocus bool
- config Config
- inset f32.Point
- scale float32
- animating bool
- // animRequested tracks whether a requestAnimationFrame callback
- // is pending.
- animRequested bool
- wakeups chan struct{}
- contextStatus contextStatus
- }
- func newWindow(win *callbacks, options []Option) {
- doc := js.Global().Get("document")
- cont := getContainer(doc)
- cnv := createCanvas(doc)
- cont.Call("appendChild", cnv)
- tarea := createTextArea(doc)
- cont.Call("appendChild", tarea)
- w := &window{
- cnv: cnv,
- document: doc,
- tarea: tarea,
- window: js.Global().Get("window"),
- head: doc.Get("head"),
- clipboard: js.Global().Get("navigator").Get("clipboard"),
- wakeups: make(chan struct{}, 1),
- w: win,
- }
- w.w.SetDriver(w)
- w.requestAnimationFrame = w.window.Get("requestAnimationFrame")
- w.browserHistory = w.window.Get("history")
- w.visualViewport = w.window.Get("visualViewport")
- if w.visualViewport.IsUndefined() {
- w.visualViewport = w.window
- }
- if screen := w.window.Get("screen"); screen.Truthy() {
- w.screenOrientation = screen.Get("orientation")
- }
- w.redraw = w.funcOf(func(this js.Value, args []js.Value) interface{} {
- w.draw(false)
- return nil
- })
- w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} {
- content := args[0].String()
- w.processEvent(transfer.DataEvent{
- Type: "application/text",
- Open: func() io.ReadCloser {
- return io.NopCloser(strings.NewReader(content))
- },
- })
- return nil
- })
- w.addEventListeners()
- w.addHistory()
- w.Configure(options)
- w.blur()
- w.processEvent(JSViewEvent{Element: cont})
- w.resize()
- w.draw(true)
- }
- func getContainer(doc js.Value) js.Value {
- cont := doc.Call("getElementById", "giowindow")
- if !cont.IsNull() {
- return cont
- }
- cont = doc.Call("createElement", "DIV")
- doc.Get("body").Call("appendChild", cont)
- return cont
- }
- func createTextArea(doc js.Value) js.Value {
- tarea := doc.Call("createElement", "input")
- style := tarea.Get("style")
- style.Set("width", "1px")
- style.Set("height", "1px")
- style.Set("opacity", "0")
- style.Set("border", "0")
- style.Set("padding", "0")
- tarea.Set("autocomplete", "off")
- tarea.Set("autocorrect", "off")
- tarea.Set("autocapitalize", "off")
- tarea.Set("spellcheck", false)
- return tarea
- }
- func createCanvas(doc js.Value) js.Value {
- cnv := doc.Call("createElement", "canvas")
- style := cnv.Get("style")
- style.Set("position", "fixed")
- style.Set("width", "100%")
- style.Set("height", "100%")
- return cnv
- }
- func (w *window) cleanup() {
- // Cleanup in the opposite order of
- // construction.
- for i := len(w.cleanfuncs) - 1; i >= 0; i-- {
- w.cleanfuncs[i]()
- }
- w.cleanfuncs = nil
- }
- func (w *window) addEventListeners() {
- w.addEventListener(w.cnv, "webglcontextlost", func(this js.Value, args []js.Value) interface{} {
- args[0].Call("preventDefault")
- w.contextStatus = contextStatusLost
- return nil
- })
- w.addEventListener(w.cnv, "webglcontextrestored", func(this js.Value, args []js.Value) interface{} {
- args[0].Call("preventDefault")
- w.contextStatus = contextStatusRestored
- // Resize is required to force update the canvas content when restored.
- w.cnv.Set("width", 0)
- w.cnv.Set("height", 0)
- w.resize()
- w.draw(true)
- return nil
- })
- w.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} {
- w.resize()
- w.draw(true)
- return nil
- })
- w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} {
- args[0].Call("preventDefault")
- return nil
- })
- w.addEventListener(w.window, "popstate", func(this js.Value, args []js.Value) interface{} {
- if w.processEvent(key.Event{Name: key.NameBack}) {
- return w.browserHistory.Call("forward")
- }
- return w.browserHistory.Call("back")
- })
- w.addEventListener(w.cnv, "mousemove", func(this js.Value, args []js.Value) interface{} {
- w.pointerEvent(pointer.Move, 0, 0, args[0])
- return nil
- })
- w.addEventListener(w.cnv, "mousedown", func(this js.Value, args []js.Value) interface{} {
- w.pointerEvent(pointer.Press, 0, 0, args[0])
- if w.requestFocus {
- w.focus()
- w.requestFocus = false
- }
- return nil
- })
- w.addEventListener(w.cnv, "mouseup", func(this js.Value, args []js.Value) interface{} {
- w.pointerEvent(pointer.Release, 0, 0, args[0])
- return nil
- })
- w.addEventListener(w.cnv, "wheel", func(this js.Value, args []js.Value) interface{} {
- e := args[0]
- dx, dy := e.Get("deltaX").Float(), e.Get("deltaY").Float()
- // horizontal scroll if shift is pressed.
- if e.Get("shiftKey").Bool() {
- dx, dy = dy, dx
- }
- mode := e.Get("deltaMode").Int()
- switch mode {
- case 0x01: // DOM_DELTA_LINE
- dx *= 10
- dy *= 10
- case 0x02: // DOM_DELTA_PAGE
- dx *= 120
- dy *= 120
- }
- w.pointerEvent(pointer.Scroll, float32(dx), float32(dy), e)
- return nil
- })
- w.addEventListener(w.cnv, "touchstart", func(this js.Value, args []js.Value) interface{} {
- w.touchEvent(pointer.Press, args[0])
- if w.requestFocus {
- w.focus() // iOS can only focus inside a Touch event.
- w.requestFocus = false
- }
- return nil
- })
- w.addEventListener(w.cnv, "touchend", func(this js.Value, args []js.Value) interface{} {
- w.touchEvent(pointer.Release, args[0])
- return nil
- })
- w.addEventListener(w.cnv, "touchmove", func(this js.Value, args []js.Value) interface{} {
- w.touchEvent(pointer.Move, args[0])
- return nil
- })
- w.addEventListener(w.cnv, "touchcancel", func(this js.Value, args []js.Value) interface{} {
- // Cancel all touches even if only one touch was cancelled.
- for i := range w.touches {
- w.touches[i] = js.Null()
- }
- w.touches = w.touches[:0]
- w.processEvent(pointer.Event{
- Kind: pointer.Cancel,
- Source: pointer.Touch,
- })
- return nil
- })
- w.addEventListener(w.tarea, "focus", func(this js.Value, args []js.Value) interface{} {
- w.config.Focused = true
- w.processEvent(ConfigEvent{Config: w.config})
- return nil
- })
- w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} {
- w.config.Focused = false
- w.processEvent(ConfigEvent{Config: w.config})
- w.blur()
- return nil
- })
- w.addEventListener(w.tarea, "keydown", func(this js.Value, args []js.Value) interface{} {
- w.keyEvent(args[0], key.Press)
- return nil
- })
- w.addEventListener(w.tarea, "keyup", func(this js.Value, args []js.Value) interface{} {
- w.keyEvent(args[0], key.Release)
- return nil
- })
- w.addEventListener(w.tarea, "compositionstart", func(this js.Value, args []js.Value) interface{} {
- w.composing = true
- return nil
- })
- w.addEventListener(w.tarea, "compositionend", func(this js.Value, args []js.Value) interface{} {
- w.composing = false
- w.flushInput()
- return nil
- })
- w.addEventListener(w.tarea, "input", func(this js.Value, args []js.Value) interface{} {
- if w.composing {
- return nil
- }
- w.flushInput()
- return nil
- })
- w.addEventListener(w.tarea, "paste", func(this js.Value, args []js.Value) interface{} {
- if w.clipboard.IsUndefined() {
- return nil
- }
- // Prevents duplicated-paste, since "paste" is already handled through Clipboard API.
- args[0].Call("preventDefault")
- return nil
- })
- }
- func (w *window) addHistory() {
- w.browserHistory.Call("pushState", nil, nil, w.window.Get("location").Get("href"))
- }
- func (w *window) flushInput() {
- val := w.tarea.Get("value").String()
- w.tarea.Set("value", "")
- w.w.EditorInsert(string(val))
- }
- func (w *window) blur() {
- w.tarea.Call("blur")
- w.requestFocus = false
- }
- func (w *window) focus() {
- w.tarea.Call("focus")
- w.requestFocus = true
- }
- func (w *window) keyboard(hint key.InputHint) {
- var m string
- switch hint {
- case key.HintAny:
- m = "text"
- case key.HintText:
- m = "text"
- case key.HintNumeric:
- m = "decimal"
- case key.HintEmail:
- m = "email"
- case key.HintURL:
- m = "url"
- case key.HintTelephone:
- m = "tel"
- case key.HintPassword:
- m = "password"
- default:
- m = "text"
- }
- w.tarea.Set("inputMode", m)
- }
- func (w *window) keyEvent(e js.Value, ks key.State) {
- k := e.Get("key").String()
- if n, ok := translateKey(k); ok {
- cmd := key.Event{
- Name: n,
- Modifiers: modifiersFor(e),
- State: ks,
- }
- w.processEvent(cmd)
- }
- }
- func (w *window) ProcessEvent(e event.Event) {
- w.processEvent(e)
- }
- func (w *window) processEvent(e event.Event) bool {
- if !w.w.ProcessEvent(e) {
- return false
- }
- select {
- case w.wakeups <- struct{}{}:
- default:
- }
- return true
- }
- func (w *window) Event() event.Event {
- for {
- evt, ok := w.w.nextEvent()
- if ok {
- if _, destroy := evt.(DestroyEvent); destroy {
- w.cleanup()
- }
- return evt
- }
- <-w.wakeups
- }
- }
- func (w *window) Invalidate() {
- w.w.Invalidate()
- }
- func (w *window) Run(f func()) {
- f()
- }
- func (w *window) Frame(frame *op.Ops) {
- w.w.ProcessFrame(frame, nil)
- }
- // modifiersFor returns the modifier set for a DOM MouseEvent or
- // KeyEvent.
- func modifiersFor(e js.Value) key.Modifiers {
- var mods key.Modifiers
- if e.Get("getModifierState").IsUndefined() {
- // Some browsers doesn't support getModifierState.
- return mods
- }
- if e.Call("getModifierState", "Alt").Bool() {
- mods |= key.ModAlt
- }
- if e.Call("getModifierState", "Control").Bool() {
- mods |= key.ModCtrl
- }
- if e.Call("getModifierState", "Shift").Bool() {
- mods |= key.ModShift
- }
- return mods
- }
- func (w *window) touchEvent(kind pointer.Kind, e js.Value) {
- e.Call("preventDefault")
- t := time.Duration(e.Get("timeStamp").Int()) * time.Millisecond
- changedTouches := e.Get("changedTouches")
- n := changedTouches.Length()
- rect := w.cnv.Call("getBoundingClientRect")
- scale := w.scale
- var mods key.Modifiers
- if e.Get("shiftKey").Bool() {
- mods |= key.ModShift
- }
- if e.Get("altKey").Bool() {
- mods |= key.ModAlt
- }
- if e.Get("ctrlKey").Bool() {
- mods |= key.ModCtrl
- }
- for i := 0; i < n; i++ {
- touch := changedTouches.Index(i)
- pid := w.touchIDFor(touch)
- x, y := touch.Get("clientX").Float(), touch.Get("clientY").Float()
- x -= rect.Get("left").Float()
- y -= rect.Get("top").Float()
- pos := f32.Point{
- X: float32(x) * scale,
- Y: float32(y) * scale,
- }
- w.processEvent(pointer.Event{
- Kind: kind,
- Source: pointer.Touch,
- Position: pos,
- PointerID: pid,
- Time: t,
- Modifiers: mods,
- })
- }
- }
- func (w *window) touchIDFor(touch js.Value) pointer.ID {
- id := touch.Get("identifier")
- for i, id2 := range w.touches {
- if id2.Equal(id) {
- return pointer.ID(i)
- }
- }
- pid := pointer.ID(len(w.touches))
- w.touches = append(w.touches, id)
- return pid
- }
- func (w *window) pointerEvent(kind pointer.Kind, dx, dy float32, e js.Value) {
- e.Call("preventDefault")
- x, y := e.Get("clientX").Float(), e.Get("clientY").Float()
- rect := w.cnv.Call("getBoundingClientRect")
- x -= rect.Get("left").Float()
- y -= rect.Get("top").Float()
- scale := w.scale
- pos := f32.Point{
- X: float32(x) * scale,
- Y: float32(y) * scale,
- }
- scroll := f32.Point{
- X: dx * scale,
- Y: dy * scale,
- }
- t := time.Duration(e.Get("timeStamp").Int()) * time.Millisecond
- jbtns := e.Get("buttons").Int()
- var btns pointer.Buttons
- if jbtns&1 != 0 {
- btns |= pointer.ButtonPrimary
- }
- if jbtns&2 != 0 {
- btns |= pointer.ButtonSecondary
- }
- if jbtns&4 != 0 {
- btns |= pointer.ButtonTertiary
- }
- w.processEvent(pointer.Event{
- Kind: kind,
- Source: pointer.Mouse,
- Buttons: btns,
- Position: pos,
- Scroll: scroll,
- Time: t,
- Modifiers: modifiersFor(e),
- })
- }
- func (w *window) addEventListener(this js.Value, event string, f func(this js.Value, args []js.Value) interface{}) {
- jsf := w.funcOf(f)
- this.Call("addEventListener", event, jsf)
- w.cleanfuncs = append(w.cleanfuncs, func() {
- this.Call("removeEventListener", event, jsf)
- })
- }
- // funcOf is like js.FuncOf but adds the js.Func to a list of
- // functions to be released during cleanup.
- func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.Func {
- jsf := js.FuncOf(f)
- w.cleanfuncs = append(w.cleanfuncs, jsf.Release)
- return jsf
- }
- func (w *window) EditorStateChanged(old, new editorState) {}
- func (w *window) SetAnimating(anim bool) {
- w.animating = anim
- if anim && !w.animRequested {
- w.animRequested = true
- w.requestAnimationFrame.Invoke(w.redraw)
- }
- }
- func (w *window) ReadClipboard() {
- if w.clipboard.IsUndefined() {
- return
- }
- if w.clipboard.Get("readText").IsUndefined() {
- return
- }
- w.clipboard.Call("readText", w.clipboard).Call("then", w.clipboardCallback)
- }
- func (w *window) WriteClipboard(mime string, s []byte) {
- if w.clipboard.IsUndefined() {
- return
- }
- if w.clipboard.Get("writeText").IsUndefined() {
- return
- }
- w.clipboard.Call("writeText", string(s))
- }
- func (w *window) Configure(options []Option) {
- prev := w.config
- cnf := w.config
- cnf.apply(unit.Metric{}, options)
- // Decorations are never disabled.
- cnf.Decorated = true
- if prev.Title != cnf.Title {
- w.config.Title = cnf.Title
- w.document.Set("title", cnf.Title)
- }
- if prev.Mode != cnf.Mode {
- w.windowMode(cnf.Mode)
- }
- if prev.NavigationColor != cnf.NavigationColor {
- w.config.NavigationColor = cnf.NavigationColor
- w.navigationColor(cnf.NavigationColor)
- }
- if prev.Orientation != cnf.Orientation {
- w.config.Orientation = cnf.Orientation
- w.orientation(cnf.Orientation)
- }
- if cnf.Decorated != prev.Decorated {
- w.config.Decorated = cnf.Decorated
- }
- w.processEvent(ConfigEvent{Config: w.config})
- }
- func (w *window) Perform(system.Action) {}
- var webCursor = [...]string{
- pointer.CursorDefault: "default",
- pointer.CursorNone: "none",
- pointer.CursorText: "text",
- pointer.CursorVerticalText: "vertical-text",
- pointer.CursorPointer: "pointer",
- pointer.CursorCrosshair: "crosshair",
- pointer.CursorAllScroll: "all-scroll",
- pointer.CursorColResize: "col-resize",
- pointer.CursorRowResize: "row-resize",
- pointer.CursorGrab: "grab",
- pointer.CursorGrabbing: "grabbing",
- pointer.CursorNotAllowed: "not-allowed",
- pointer.CursorWait: "wait",
- pointer.CursorProgress: "progress",
- pointer.CursorNorthWestResize: "nw-resize",
- pointer.CursorNorthEastResize: "ne-resize",
- pointer.CursorSouthWestResize: "sw-resize",
- pointer.CursorSouthEastResize: "se-resize",
- pointer.CursorNorthSouthResize: "ns-resize",
- pointer.CursorEastWestResize: "ew-resize",
- pointer.CursorWestResize: "w-resize",
- pointer.CursorEastResize: "e-resize",
- pointer.CursorNorthResize: "n-resize",
- pointer.CursorSouthResize: "s-resize",
- pointer.CursorNorthEastSouthWestResize: "nesw-resize",
- pointer.CursorNorthWestSouthEastResize: "nwse-resize",
- }
- func (w *window) SetCursor(cursor pointer.Cursor) {
- style := w.cnv.Get("style")
- style.Set("cursor", webCursor[cursor])
- }
- func (w *window) ShowTextInput(show bool) {
- // Run in a goroutine to avoid a deadlock if the
- // focus change result in an event.
- if show {
- w.focus()
- } else {
- w.blur()
- }
- }
- func (w *window) SetInputHint(mode key.InputHint) {
- w.keyboard(mode)
- }
- func (w *window) resize() {
- w.scale = float32(w.window.Get("devicePixelRatio").Float())
- rect := w.cnv.Call("getBoundingClientRect")
- size := image.Point{
- X: int(float32(rect.Get("width").Float()) * w.scale),
- Y: int(float32(rect.Get("height").Float()) * w.scale),
- }
- if size != w.config.Size {
- w.config.Size = size
- w.processEvent(ConfigEvent{Config: w.config})
- }
- if vx, vy := w.visualViewport.Get("width"), w.visualViewport.Get("height"); !vx.IsUndefined() && !vy.IsUndefined() {
- w.inset.X = float32(w.config.Size.X) - float32(vx.Float())*w.scale
- w.inset.Y = float32(w.config.Size.Y) - float32(vy.Float())*w.scale
- }
- if w.config.Size.X == 0 || w.config.Size.Y == 0 {
- return
- }
- w.cnv.Set("width", w.config.Size.X)
- w.cnv.Set("height", w.config.Size.Y)
- }
- func (w *window) draw(sync bool) {
- if w.contextStatus == contextStatusLost {
- return
- }
- anim := w.animating
- w.animRequested = anim
- if anim {
- w.requestAnimationFrame.Invoke(w.redraw)
- } else if !sync {
- return
- }
- size, insets, metric := w.getConfig()
- if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 {
- return
- }
- w.processEvent(frameEvent{
- FrameEvent: FrameEvent{
- Now: time.Now(),
- Size: size,
- Insets: insets,
- Metric: metric,
- },
- Sync: sync,
- })
- }
- func (w *window) getConfig() (image.Point, Insets, unit.Metric) {
- invscale := unit.Dp(1. / w.scale)
- return image.Pt(w.config.Size.X, w.config.Size.Y),
- Insets{
- Bottom: unit.Dp(w.inset.Y) * invscale,
- Right: unit.Dp(w.inset.X) * invscale,
- }, unit.Metric{
- PxPerDp: w.scale,
- PxPerSp: w.scale,
- }
- }
- func (w *window) windowMode(mode WindowMode) {
- switch mode {
- case Windowed:
- if !w.document.Get("fullscreenElement").Truthy() {
- return // Browser is already Windowed.
- }
- if !w.document.Get("exitFullscreen").Truthy() {
- return // Browser doesn't support such feature.
- }
- w.document.Call("exitFullscreen")
- w.config.Mode = Windowed
- case Fullscreen:
- elem := w.document.Get("documentElement")
- if !elem.Get("requestFullscreen").Truthy() {
- return // Browser doesn't support such feature.
- }
- elem.Call("requestFullscreen")
- w.config.Mode = Fullscreen
- }
- }
- func (w *window) orientation(mode Orientation) {
- if j := w.screenOrientation; !j.Truthy() || !j.Get("unlock").Truthy() || !j.Get("lock").Truthy() {
- return // Browser don't support Screen Orientation API.
- }
- switch mode {
- case AnyOrientation:
- w.screenOrientation.Call("unlock")
- case LandscapeOrientation:
- w.screenOrientation.Call("lock", "landscape").Call("then", w.redraw)
- case PortraitOrientation:
- w.screenOrientation.Call("lock", "portrait").Call("then", w.redraw)
- }
- }
- func (w *window) navigationColor(c color.NRGBA) {
- theme := w.head.Call("querySelector", `meta[name="theme-color"]`)
- if !theme.Truthy() {
- theme = w.document.Call("createElement", "meta")
- theme.Set("name", "theme-color")
- w.head.Call("appendChild", theme)
- }
- rgba := f32color.NRGBAToRGBA(c)
- theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B}))
- }
- func osMain() {
- select {}
- }
- func translateKey(k string) (key.Name, bool) {
- var n key.Name
- switch k {
- case "ArrowUp":
- n = key.NameUpArrow
- case "ArrowDown":
- n = key.NameDownArrow
- case "ArrowLeft":
- n = key.NameLeftArrow
- case "ArrowRight":
- n = key.NameRightArrow
- case "Escape":
- n = key.NameEscape
- case "Enter":
- n = key.NameReturn
- case "Backspace":
- n = key.NameDeleteBackward
- case "Delete":
- n = key.NameDeleteForward
- case "Home":
- n = key.NameHome
- case "End":
- n = key.NameEnd
- case "PageUp":
- n = key.NamePageUp
- case "PageDown":
- n = key.NamePageDown
- case "Tab":
- n = key.NameTab
- case " ":
- n = key.NameSpace
- case "F1":
- n = key.NameF1
- case "F2":
- n = key.NameF2
- case "F3":
- n = key.NameF3
- case "F4":
- n = key.NameF4
- case "F5":
- n = key.NameF5
- case "F6":
- n = key.NameF6
- case "F7":
- n = key.NameF7
- case "F8":
- n = key.NameF8
- case "F9":
- n = key.NameF9
- case "F10":
- n = key.NameF10
- case "F11":
- n = key.NameF11
- case "F12":
- n = key.NameF12
- case "Control":
- n = key.NameCtrl
- case "Shift":
- n = key.NameShift
- case "Alt":
- n = key.NameAlt
- case "OS":
- n = key.NameSuper
- default:
- r, s := utf8.DecodeRuneInString(k)
- // If there is exactly one printable character, return that.
- if s == len(k) && unicode.IsPrint(r) {
- return key.Name(strings.ToUpper(k)), true
- }
- return "", false
- }
- return n, true
- }
- func (JSViewEvent) implementsViewEvent() {}
- func (JSViewEvent) ImplementsEvent() {}
- func (j JSViewEvent) Valid() bool {
- return !(j.Element.IsNull() || j.Element.IsUndefined())
- }
|