311 lines
8.9 KiB
Go
311 lines
8.9 KiB
Go
|
// go:build windows || darwin
|
||
|
package giouipinentry
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"image"
|
||
|
"image/color"
|
||
|
"log"
|
||
|
|
||
|
"gioui.org/app"
|
||
|
"gioui.org/font/gofont"
|
||
|
"gioui.org/io/key"
|
||
|
"gioui.org/io/system"
|
||
|
"gioui.org/layout"
|
||
|
"gioui.org/op"
|
||
|
"gioui.org/op/clip"
|
||
|
"gioui.org/op/paint"
|
||
|
"gioui.org/unit"
|
||
|
"gioui.org/widget"
|
||
|
"gioui.org/widget/material"
|
||
|
)
|
||
|
|
||
|
func GetPin(title string, description string, onPinEntered func(string)) {
|
||
|
go func() {
|
||
|
w := app.NewWindow()
|
||
|
w.Option(app.Size(unit.Dp(500), unit.Dp(300)))
|
||
|
w.Option(app.Decorated(false))
|
||
|
w.Perform(system.ActionCenter)
|
||
|
w.Perform(system.ActionRaise)
|
||
|
if err := runPinentry(w, title, description, onPinEntered); err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
func GetApproval(title string, description string, onApproval func(bool)) {
|
||
|
go func() {
|
||
|
w := app.NewWindow()
|
||
|
w.Option(app.Size(unit.Dp(500), unit.Dp(300)))
|
||
|
w.Option(app.Decorated(false))
|
||
|
w.Perform(system.ActionCenter)
|
||
|
w.Perform(system.ActionRaise)
|
||
|
if err := runApproval(w, title, description, onApproval); err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
// #000000
|
||
|
unselected = color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xFF}
|
||
|
// #FFFFFF
|
||
|
unselectedText = color.NRGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}
|
||
|
// #000000
|
||
|
background = color.NRGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xFF}
|
||
|
// #651FFF
|
||
|
selected = color.NRGBA{R: 0x65, G: 0x1F, B: 0xFF, A: 0xFF}
|
||
|
// #FFFFFF
|
||
|
selectedText = color.NRGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}
|
||
|
// #4caf50
|
||
|
buttonOk = color.NRGBA{R: 0x4C, G: 0xAF, B: 0x50, A: 0xFF}
|
||
|
// #F44336
|
||
|
buttonCancel = color.NRGBA{R: 0xF4, G: 0x43, B: 0x36, A: 0xFF}
|
||
|
)
|
||
|
|
||
|
var th = material.NewTheme(gofont.Collection())
|
||
|
|
||
|
func runPinentry(w *app.Window, title string, description string, onPinEntered func(string)) error {
|
||
|
var lineEditor = &widget.Editor{
|
||
|
SingleLine: true,
|
||
|
Submit: true,
|
||
|
}
|
||
|
lineEditor.Focus()
|
||
|
var ops op.Ops
|
||
|
|
||
|
var btnOk widget.Clickable
|
||
|
var btnCancel widget.Clickable
|
||
|
|
||
|
for {
|
||
|
e := <-w.Events()
|
||
|
switch e := e.(type) {
|
||
|
case system.DestroyEvent:
|
||
|
return e.Err
|
||
|
case system.FrameEvent:
|
||
|
gtx := layout.NewContext(&ops, e)
|
||
|
|
||
|
key.InputOp{
|
||
|
Keys: key.Set(key.NameReturn + "|" + key.NameEscape + "|" + key.NameDownArrow),
|
||
|
Tag: 0,
|
||
|
}.Add(gtx.Ops)
|
||
|
t := lineEditor.Events()
|
||
|
for _, ev := range t {
|
||
|
switch ev.(type) {
|
||
|
case widget.SubmitEvent:
|
||
|
onPinEntered(lineEditor.Text())
|
||
|
w.Perform(system.ActionClose)
|
||
|
}
|
||
|
}
|
||
|
if btnOk.Clicked() {
|
||
|
onPinEntered(lineEditor.Text())
|
||
|
w.Perform(system.ActionClose)
|
||
|
}
|
||
|
if btnCancel.Clicked() {
|
||
|
onPinEntered("")
|
||
|
w.Perform(system.ActionClose)
|
||
|
}
|
||
|
|
||
|
test := gtx.Events(0)
|
||
|
for _, ev := range test {
|
||
|
switch ev := ev.(type) {
|
||
|
case key.Event:
|
||
|
switch ev.Name {
|
||
|
case key.NameReturn:
|
||
|
fmt.Println("uncaught submit")
|
||
|
return nil
|
||
|
case key.NameEscape:
|
||
|
onPinEntered("")
|
||
|
w.Perform(system.ActionClose)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||
|
//title
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
title := material.H6(th, title)
|
||
|
title.Color = unselectedText
|
||
|
return title.Layout(gtx)
|
||
|
})
|
||
|
})
|
||
|
}),
|
||
|
// description
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
body := material.Body1(th, description)
|
||
|
body.Color = unselectedText
|
||
|
return body.Layout(gtx)
|
||
|
})
|
||
|
})
|
||
|
}),
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
searchBox := material.Editor(th, lineEditor, "Pin")
|
||
|
searchBox.Color = selectedText
|
||
|
border := widget.Border{Color: selectedText, CornerRadius: unit.Dp(8), Width: unit.Dp(2)}
|
||
|
return border.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(8)).Layout(gtx, searchBox.Layout)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
}),
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.Flex{Axis: layout.Vertical, Spacing: layout.SpaceStart}.Layout(gtx,
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
acceptBtn := material.Button(th, &btnOk, "Accept")
|
||
|
acceptBtn.Background = buttonOk
|
||
|
return acceptBtn.Layout(gtx)
|
||
|
}),
|
||
|
layout.Rigid(
|
||
|
layout.Spacer{Height: unit.Dp(10)}.Layout,
|
||
|
),
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
cancel := material.Button(th, &btnCancel, "Cancel")
|
||
|
cancel.Background = buttonCancel
|
||
|
return cancel.Layout(gtx)
|
||
|
}),
|
||
|
)
|
||
|
})
|
||
|
}),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
e.Frame(gtx.Ops)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func runApproval(w *app.Window, title string, description string, onApproval func(bool)) error {
|
||
|
var ops op.Ops
|
||
|
|
||
|
var btnOk widget.Clickable
|
||
|
var btnCancel widget.Clickable
|
||
|
|
||
|
for {
|
||
|
e := <-w.Events()
|
||
|
switch e := e.(type) {
|
||
|
case system.DestroyEvent:
|
||
|
return e.Err
|
||
|
case system.FrameEvent:
|
||
|
gtx := layout.NewContext(&ops, e)
|
||
|
|
||
|
key.InputOp{
|
||
|
Keys: key.Set(key.NameReturn + "|" + key.NameEscape + "|" + key.NameDownArrow),
|
||
|
Tag: 0,
|
||
|
}.Add(gtx.Ops)
|
||
|
|
||
|
if btnOk.Clicked() {
|
||
|
onApproval(true)
|
||
|
w.Perform(system.ActionClose)
|
||
|
}
|
||
|
if btnCancel.Clicked() {
|
||
|
onApproval(false)
|
||
|
w.Perform(system.ActionClose)
|
||
|
}
|
||
|
|
||
|
test := gtx.Events(0)
|
||
|
for _, ev := range test {
|
||
|
switch ev := ev.(type) {
|
||
|
case key.Event:
|
||
|
switch ev.Name {
|
||
|
case key.NameReturn:
|
||
|
fmt.Println("uncaught submit")
|
||
|
return nil
|
||
|
case key.NameEscape:
|
||
|
onApproval(false)
|
||
|
w.Perform(system.ActionClose)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||
|
//title
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
title := material.H6(th, title)
|
||
|
title.Color = unselectedText
|
||
|
return title.Layout(gtx)
|
||
|
})
|
||
|
})
|
||
|
}),
|
||
|
// description
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
return Background{Color: background, CornerRadius: unit.Dp(0)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
body := material.Body1(th, description)
|
||
|
body.Color = unselectedText
|
||
|
return body.Layout(gtx)
|
||
|
})
|
||
|
})
|
||
|
}),
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||
|
return layout.Flex{Axis: layout.Vertical, Spacing: layout.SpaceStart}.Layout(gtx,
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
acceptBtn := material.Button(th, &btnOk, "Accept")
|
||
|
acceptBtn.Background = buttonOk
|
||
|
return acceptBtn.Layout(gtx)
|
||
|
}),
|
||
|
layout.Rigid(
|
||
|
layout.Spacer{Height: unit.Dp(10)}.Layout,
|
||
|
),
|
||
|
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||
|
cancel := material.Button(th, &btnCancel, "Cancel")
|
||
|
cancel.Background = buttonCancel
|
||
|
return cancel.Layout(gtx)
|
||
|
}),
|
||
|
)
|
||
|
})
|
||
|
}),
|
||
|
)
|
||
|
})
|
||
|
|
||
|
e.Frame(gtx.Ops)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type Background struct {
|
||
|
Color color.NRGBA
|
||
|
CornerRadius unit.Dp
|
||
|
}
|
||
|
|
||
|
func (b Background) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
||
|
m := op.Record(gtx.Ops)
|
||
|
dims := w(gtx)
|
||
|
size := dims.Size
|
||
|
call := m.Stop()
|
||
|
if r := gtx.Dp(b.CornerRadius); r > 0 {
|
||
|
defer clip.RRect{
|
||
|
Rect: image.Rect(0, 0, size.X, size.Y),
|
||
|
NE: r, NW: r, SE: r, SW: r,
|
||
|
}.Push(gtx.Ops).Pop()
|
||
|
}
|
||
|
fill{b.Color}.Layout(gtx, size)
|
||
|
call.Add(gtx.Ops)
|
||
|
return dims
|
||
|
}
|
||
|
|
||
|
type fill struct {
|
||
|
col color.NRGBA
|
||
|
}
|
||
|
|
||
|
func (f fill) Layout(gtx layout.Context, sz image.Point) layout.Dimensions {
|
||
|
defer clip.Rect(image.Rectangle{Max: sz}).Push(gtx.Ops).Pop()
|
||
|
paint.ColorOp{Color: f.col}.Add(gtx.Ops)
|
||
|
paint.PaintOp{}.Add(gtx.Ops)
|
||
|
return layout.Dimensions{Size: sz}
|
||
|
}
|