add noto fonts

This commit is contained in:
ginuerzh
2024-06-03 22:23:28 +08:00
parent 64838e272f
commit 42846b8e7b
33 changed files with 514 additions and 130 deletions
+2 -1
View File
@@ -37,7 +37,8 @@ windows-arm64:
# go install gioui.org/cmd/gogio@latest
android:
gogio -x -work -target android -minsdk 22 -version $(VERSION).7 -name GOST+ -signkey build/sign.keystore -signpass android -appid gost.plus -o $(BINDIR)/$(NAME)-$(VERSION).aab .
gogio -x -work -target android -minsdk 22 -version $(VERSION).8 -name GOST+ -signkey build/sign.keystore -signpass android -appid gost.plus -o $(BINDIR)/$(NAME)-$(VERSION).aab .
gogio -x -work -target android -minsdk 22 -version $(VERSION).8 -name GOST+ -signkey build/sign.keystore -signpass android -appid gost.plus -o $(BINDIR)/$(NAME)-$(VERSION).apk .
gz_releases=$(addsuffix .gz, $(PLATFORM_LIST))
zip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST))
+11 -9
View File
@@ -1,6 +1,8 @@
module github.com/go-gost/gost.plus
go 1.22.0
go 1.22
toolchain go1.22.2
require (
gioui.org v0.6.0
@@ -26,7 +28,7 @@ require (
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-text/typesetting v0.1.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
@@ -36,7 +38,7 @@ require (
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/rs/xid v1.3.0 // indirect
@@ -52,14 +54,14 @@ require (
github.com/xtaci/smux v1.5.24 // indirect
github.com/yl2chen/cidranger v1.0.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/image v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)
+18 -23
View File
@@ -44,12 +44,10 @@ github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04/go.mod h
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
@@ -83,8 +81,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
@@ -126,29 +124,26 @@ github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+Seva
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/exp/shiny v0.0.0-20240103183307-be819d1f06fc h1:OG+uKOKt/BW+ydf/M7gym7ONo8U+dyIlLazys3du298=
golang.org/x/exp/shiny v0.0.0-20240103183307-be819d1f06fc/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+39 -14
View File
@@ -2,6 +2,8 @@ package main
import (
"context"
"fmt"
"log/slog"
_ "net"
"os"
"time"
@@ -16,6 +18,9 @@ import (
"github.com/go-gost/gost.plus/tunnel"
"github.com/go-gost/gost.plus/tunnel/entrypoint"
"github.com/go-gost/gost.plus/ui"
"github.com/go-gost/gost.plus/ui/page"
"github.com/go-gost/gost.plus/ui/theme"
"github.com/go-gost/gost.plus/ui/widget"
_ "github.com/go-gost/gost.plus/winres"
)
@@ -23,11 +28,7 @@ func main() {
Init()
go func() {
var w app.Window
w.Option(app.Title("GOST+"))
w.Option(app.MinSize(800, 600))
err := run(&w)
if err != nil {
if err := run(); err != nil {
logger.Default().Fatal(err)
}
os.Exit(0)
@@ -35,16 +36,12 @@ func main() {
app.Main()
}
func run(w *app.Window) error {
go func() {
for e := range runner.Event() {
if e.TaskID == runner.TaskUpdateStats {
w.Invalidate()
}
}
}()
func run() error {
ui := ui.NewUI()
go handleEvent(ui)
w := ui.Window()
var ops op.Ops
for {
switch e := w.Event().(type) {
@@ -60,6 +57,34 @@ func run(w *app.Window) error {
}
}
func handleEvent(ui *ui.UI) {
for {
select {
case e := <-ui.Router().Event():
switch e.ID {
case page.EventThemeChanged:
slog.Debug("theme changed", "event", e.ID)
ui.Window().Option(app.StatusColor(theme.Current().Material.Bg))
}
case e := <-runner.Event():
switch e.TaskID {
case runner.TaskUpdateStats:
ui.Window().Invalidate()
default:
if e.Err != nil {
slog.Error(fmt.Sprintf("task: %s", e.Err), "task", e.TaskID)
ui.Router().Notify(widget.Message{
Type: widget.Error,
Content: e.Err.Error(),
})
}
}
}
}
}
func Init() {
config.Init()
tunnel.LoadConfig()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+45
View File
@@ -0,0 +1,45 @@
package fonts
import (
_ "embed"
"fmt"
"gioui.org/font"
"gioui.org/font/opentype"
)
var (
//go:embed NotoSans-Regular.ttf
notoSansRegular []byte
//go:embed NotoSans-SemiBold.ttf
notoSansSemiBold []byte
//go:embed NotoSansSC-Regular.ttf
notoSansSCRegular []byte
//go:embed NotoSansSC-SemiBold.ttf
notoSansSCSemiBold []byte
)
var (
collection []font.FontFace
)
func init() {
register(notoSansRegular)
register(notoSansSemiBold)
register(notoSansSCRegular)
register(notoSansSCSemiBold)
}
func Collection() []font.FontFace {
return collection
}
func register(ttf []byte) {
faces, err := opentype.ParseCollection(ttf)
if err != nil {
panic(fmt.Errorf("failed to parse font: %v", err))
}
collection = append(collection, faces...)
}
+1
View File
@@ -60,6 +60,7 @@ var (
IconActionUpdate = mustIcon(icons.ActionUpdate)
IconActionHourGlassEmpty = mustIcon(icons.ActionHourglassEmpty)
IconInfo = mustIcon(icons.ActionInfo)
IconAlert = mustIcon(icons.AlertErrorOutline)
)
func mustIcon(data []byte) *widget.Icon {
+8
View File
@@ -342,6 +342,10 @@ func (p *tcpPage) create() error {
if err := ep.Run(); err != nil {
ep.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
return err
}
@@ -368,6 +372,10 @@ func (p *tcpPage) update(opts ...tunnel.Option) tunnel.Tunnel {
if err := ep.Run(); err != nil {
ep.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
logger.Default().Error(err)
}
+8
View File
@@ -399,6 +399,10 @@ func (p *udpPage) create() error {
if err := ep.Run(); err != nil {
ep.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
return err
}
@@ -429,6 +433,10 @@ func (p *udpPage) update(opts ...tunnel.Option) tunnel.Tunnel {
if err := ep.Run(); err != nil {
ep.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
logger.Default().Error(err)
}
+11
View File
@@ -0,0 +1,11 @@
package page
type EventID string
const (
EventThemeChanged EventID = "event.theme.changed"
)
type Event struct {
ID EventID
}
+1 -9
View File
@@ -4,7 +4,6 @@ import (
"image/color"
"sync/atomic"
"gioui.org/font"
"gioui.org/layout"
"gioui.org/widget"
"gioui.org/widget/material"
@@ -90,14 +89,7 @@ func (p *homePage) Layout(gtx C) D {
gtx.Constraints.Max.X = gtx.Dp(50)
return icons.IconApp.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Width: 8}.Layout),
layout.Flexed(1, func(gtx C) D {
label := material.H6(th, "GOST+")
label.Font.Weight = font.SemiBold
return label.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Width: 8}.Layout),
layout.Flexed(1, layout.Spacer{Width: 8}.Layout),
layout.Rigid(func(gtx C) D {
if p.btnFavorite.Clicked(gtx) {
p.favorite.Store(!p.favorite.Load())
+5
View File
@@ -2,6 +2,7 @@ package page
import (
"gioui.org/layout"
"gioui.org/widget/material"
)
type PagePath string
@@ -27,6 +28,10 @@ type PageOptions struct {
type PageOption func(opts *PageOptions)
type C = layout.Context
type D = layout.Dimensions
type T = material.Theme
func WithPageID(id string) PageOption {
return func(opts *PageOptions) {
opts.ID = id
+66 -8
View File
@@ -1,6 +1,11 @@
package page
import (
"time"
"gioui.org/app"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
@@ -10,33 +15,39 @@ import (
"gioui.org/x/component"
"github.com/go-gost/core/logger"
"github.com/go-gost/gost.plus/ui/theme"
ui_widget "github.com/go-gost/gost.plus/ui/widget"
)
const (
MaxWidth = 800
)
type C = layout.Context
type D = layout.Dimensions
type Route struct {
Path PagePath
ID string
}
type Router struct {
w *app.Window
pages map[PagePath]Page
stack routeStack
current Route
*material.Theme
modal *component.ModalLayer
modal *component.ModalLayer
notification *ui_widget.Notification
events chan Event
}
func NewRouter(th *material.Theme) *Router {
func NewRouter(w *app.Window, th *T) *Router {
r := &Router{
w: w,
pages: make(map[PagePath]Page),
Theme: th,
modal: component.NewModal(),
notification: ui_widget.NewNotification(3*time.Second, func() {
w.Invalidate()
}),
events: make(chan Event, 16),
}
return r
@@ -72,7 +83,6 @@ func (r *Router) Back() {
}
r.current = route
page.Init(WithPageID(route.ID))
logger.Default().WithFields(map[string]any{
"kind": "router",
"route.id": route.ID,
@@ -80,6 +90,25 @@ func (r *Router) Back() {
}
func (r *Router) Layout(gtx C) D {
if r.stack.Peek().Path != PageHome {
event.Op(gtx.Ops, r.w)
for {
ev, ok := gtx.Event(
key.Filter{Name: key.NameBack},
key.Filter{Name: key.NameEscape},
)
if !ok {
break
}
switch ev := ev.(type) {
case key.Event:
if ev.State == key.Press {
r.Back()
}
}
}
}
r.Theme.Palette = theme.Current().Material
defer r.modal.Layout(gtx, r.Theme)
@@ -113,7 +142,21 @@ func (r *Router) Layout(gtx C) D {
}
return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return page.Layout(gtx)
return layout.Stack{
Alignment: layout.N,
}.Layout(gtx,
layout.Expanded(page.Layout),
layout.Stacked(func(gtx layout.Context) layout.Dimensions {
return layout.Inset{
Top: 16,
Bottom: 16,
Right: gtx.Metric.PxToDp(gtx.Constraints.Max.X) / 5,
Left: gtx.Metric.PxToDp(gtx.Constraints.Max.X) / 5,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return r.notification.Layout(gtx, r.Theme)
})
}),
)
})
},
)
@@ -124,7 +167,7 @@ func (r *Router) ShowModal(gtx layout.Context, w func(gtx C, th *material.Theme)
if gtx.Constraints.Max.X > gtx.Dp(MaxWidth) {
gtx.Constraints.Max.X = gtx.Dp(MaxWidth)
}
gtx.Constraints.Max.X = gtx.Constraints.Max.X * 2 / 3
gtx.Constraints.Max.X = gtx.Constraints.Max.X * 3 / 4
var clk widget.Clickable
return clk.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
@@ -138,6 +181,21 @@ func (r *Router) HideModal(gtx C) {
r.modal.Disappear(gtx.Now)
}
func (r *Router) Notify(message ui_widget.Message) {
r.notification.Show(message)
}
func (r *Router) Emit(event Event) {
select {
case r.events <- event:
default:
}
}
func (r *Router) Event() <-chan Event {
return r.events
}
type routeStack struct {
routes []Route
}
+44 -29
View File
@@ -128,50 +128,64 @@ func (p *settingsPage) Layout(gtx layout.Context) layout.Dimensions {
}
func (p *settingsPage) layout(gtx layout.Context, th *material.Theme) layout.Dimensions {
return component.SurfaceStyle{
Theme: th,
ShadowStyle: component.ShadowStyle{
CornerRadius: 12,
},
Fill: theme.Current().ContentSurfaceBg,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(16).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{
Axis: layout.Vertical,
Alignment: layout.Middle,
}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.Flex{
Axis: layout.Vertical,
Alignment: layout.Middle,
}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Max.X = gtx.Dp(60)
return icons.IconApp.Layout(gtx)
})
}),
layout.Rigid(layout.Spacer{Height: 8}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
label := material.H6(th, "GOST+")
label.Font.Weight = font.Bold
return label.Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: 8}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return material.Body1(th, version.Version).Layout(gtx)
}),
layout.Rigid(layout.Spacer{Height: 16}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if p.lang.Clicked(gtx) {
p.showLangMenu(gtx)
}
return p.lang.Layout(gtx, th)
label := material.Body1(th, "GOST+")
label.Font.Weight = font.SemiBold
return layout.Center.Layout(gtx, label.Layout)
}),
layout.Rigid(layout.Spacer{Height: 8}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if p.theme.Clicked(gtx) {
p.showThemeMenu(gtx)
}
return p.theme.Layout(gtx, th)
return layout.Center.Layout(gtx, material.Body1(th, version.Version).Layout)
}),
)
})
})
}),
layout.Rigid(layout.Spacer{Height: 32}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return component.SurfaceStyle{
Theme: th,
ShadowStyle: component.ShadowStyle{
CornerRadius: 12,
},
Fill: theme.Current().ContentSurfaceBg,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(16).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{
Axis: layout.Vertical,
}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if p.lang.Clicked(gtx) {
p.showLangMenu(gtx)
}
return p.lang.Layout(gtx, th)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if p.theme.Clicked(gtx) {
p.showThemeMenu(gtx)
}
return p.theme.Layout(gtx, th)
}),
)
})
})
}),
)
}
func (p *settingsPage) showLangMenu(gtx layout.Context) {
@@ -263,6 +277,7 @@ func (p *settingsPage) showThemeMenu(gtx layout.Context) {
default:
theme.UseLight()
}
p.router.Emit(page.Event{ID: page.EventThemeChanged})
}
p.router.ShowModal(gtx, func(gtx page.C, th *material.Theme) page.D {
+8
View File
@@ -495,6 +495,10 @@ func (p *filePage) create() error {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
return err
}
@@ -528,6 +532,10 @@ func (p *filePage) update(opts ...tunnel.Option) tunnel.Tunnel {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
logger.Default().Error(err)
}
+8
View File
@@ -553,6 +553,10 @@ func (p *httpPage) create() error {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
return err
}
@@ -592,6 +596,10 @@ func (p *httpPage) update(opts ...tunnel.Option) tunnel.Tunnel {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
logger.Default().Error(err)
}
+8
View File
@@ -398,6 +398,10 @@ func (p *tcpPage) create() error {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
return err
}
@@ -424,6 +428,10 @@ func (p *tcpPage) update(opts ...tunnel.Option) tunnel.Tunnel {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
logger.Default().Error(err)
}
+8
View File
@@ -398,6 +398,10 @@ func (p *udpPage) create() error {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
return err
}
@@ -424,6 +428,10 @@ func (p *udpPage) update(opts ...tunnel.Option) tunnel.Tunnel {
if err := tun.Run(); err != nil {
tun.Close()
p.router.Notify(ui_widget.Message{
Type: ui_widget.Error,
Content: err.Error(),
})
logger.Default().Error(err)
}
+7 -1
View File
@@ -19,6 +19,8 @@ type Palette struct {
ListBg color.NRGBA
NavButtonBg color.NRGBA
NavButtonContrastBg color.NRGBA
ItemBg color.NRGBA
NotificationBg color.NRGBA
}
type Theme struct {
@@ -41,8 +43,10 @@ var (
},
ContentSurfaceBg: color.NRGBA(colornames.Grey50),
ListBg: color.NRGBA(colornames.BlueGrey50),
NavButtonBg: color.NRGBA(colornames.White),
NavButtonBg: color.NRGBA(colornames.BlueGrey50),
NavButtonContrastBg: color.NRGBA(colornames.BlueGrey100),
ItemBg: color.NRGBA(colornames.Grey300),
NotificationBg: color.NRGBA(colornames.Grey200),
},
}
@@ -58,6 +62,8 @@ var (
ListBg: color.NRGBA(colornames.Grey700),
NavButtonBg: color.NRGBA(colornames.Grey800),
NavButtonContrastBg: color.NRGBA(colornames.Grey600),
ItemBg: color.NRGBA(colornames.Grey600),
NotificationBg: color.NRGBA(colornames.Grey700),
},
}
)
+22 -3
View File
@@ -1,11 +1,12 @@
package ui
import (
"gioui.org/font/gofont"
"gioui.org/app"
"gioui.org/layout"
"gioui.org/text"
"gioui.org/widget/material"
"github.com/go-gost/gost.plus/config"
"github.com/go-gost/gost.plus/ui/fonts"
"github.com/go-gost/gost.plus/ui/i18n"
"github.com/go-gost/gost.plus/ui/page"
"github.com/go-gost/gost.plus/ui/page/entrypoint"
@@ -25,6 +26,7 @@ type C = layout.Context
type D = layout.Dimensions
type UI struct {
w *app.Window
router *page.Router
}
@@ -40,10 +42,18 @@ func NewUI() *UI {
}
th := material.NewTheme()
th.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))
// th.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))
th.Shaper = text.NewShaper(text.WithCollection(fonts.Collection()))
th.Palette = theme.Current().Material
router := page.NewRouter(th)
w := &app.Window{}
w.Option(
app.Title("GOST"),
app.MinSize(800, 600),
app.StatusColor(th.Bg),
)
router := page.NewRouter(w, th)
router.Register(page.PageHome, home.NewPage(router))
router.Register(page.PageTunnel, tunnel.NewPage(router))
router.Register(page.PageTunnelFile, file.NewPage(router))
@@ -60,6 +70,7 @@ func NewUI() *UI {
})
return &UI{
w: w,
router: router,
}
}
@@ -67,3 +78,11 @@ func NewUI() *UI {
func (ui *UI) Layout(gtx C) D {
return ui.router.Layout(gtx)
}
func (ui *UI) Window() *app.Window {
return ui.w
}
func (ui *UI) Router() *page.Router {
return ui.router
}
+6 -6
View File
@@ -91,14 +91,14 @@ func (p *Dialog) Layout(gtx layout.Context, th *material.Theme) layout.Dimension
return material.ButtonLayoutStyle{
Background: th.Bg,
CornerRadius: 20,
CornerRadius: 18,
Button: &p.btnCancel,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Inset{
Top: 8,
Bottom: 8,
Left: 24,
Right: 24,
Left: 20,
Right: 20,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
label := material.Body1(th, i18n.Cancel.Value())
label.Color = th.Fg
@@ -117,14 +117,14 @@ func (p *Dialog) Layout(gtx layout.Context, th *material.Theme) layout.Dimension
return material.ButtonLayoutStyle{
Background: th.Bg,
CornerRadius: 20,
CornerRadius: 18,
Button: &p.btnOK,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Inset{
Top: 8,
Bottom: 8,
Left: 24,
Right: 24,
Left: 20,
Right: 20,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
label := material.Body1(th, i18n.OK.Value())
label.Color = th.Fg
+4 -4
View File
@@ -105,10 +105,10 @@ func (p *MenuItem) Layout(gtx layout.Context, th *material.Theme) layout.Dimensi
Button: &p.state,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Inset{
Top: 8,
Bottom: 8,
Left: 16,
Right: 16,
Top: 12,
Bottom: 12,
Left: 24,
Right: 24,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{
Spacing: layout.SpaceBetween,
+5 -13
View File
@@ -45,15 +45,10 @@ func (p *Nav) Layout(gtx layout.Context, th *material.Theme) layout.Dimensions {
if p.current == index {
btn.background = theme.Current().NavButtonContrastBg
} else {
btn.background = theme.Current().Material.Bg
btn.background = theme.Current().NavButtonBg
}
return layout.Inset{
Top: 8,
Bottom: 8,
Left: 12,
Right: 12,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(8).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return btn.Layout(gtx, th)
})
})
@@ -62,15 +57,13 @@ func (p *Nav) Layout(gtx layout.Context, th *material.Theme) layout.Dimensions {
type NavButton struct {
btn widget.Clickable
cornerRadius unit.Dp
borderWidth unit.Dp
background color.NRGBA
text i18n.Key
}
func NewNavButton(text i18n.Key) *NavButton {
return &NavButton{
cornerRadius: 18,
borderWidth: 1,
cornerRadius: 20,
text: text,
}
}
@@ -83,14 +76,13 @@ func (btn *NavButton) Layout(gtx layout.Context, th *material.Theme) layout.Dime
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return widget.Border{
Color: theme.Current().NavButtonContrastBg,
Width: btn.borderWidth,
CornerRadius: btn.cornerRadius,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Inset{
Top: 8,
Bottom: 8,
Left: 20,
Right: 20,
Left: 16,
Right: 16,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
label := material.Body1(th, btn.text.Value())
return label.Layout(gtx)
+122
View File
@@ -0,0 +1,122 @@
package widget
import (
"image/color"
"sync"
"sync/atomic"
"time"
"gioui.org/layout"
"gioui.org/widget/material"
"gioui.org/x/component"
"github.com/go-gost/gost.plus/ui/icons"
"github.com/go-gost/gost.plus/ui/theme"
"golang.org/x/exp/shiny/materialdesign/colornames"
)
const (
Success string = "success"
Info string = "info"
Warn string = "warn"
Error string = "error"
)
type Message struct {
Type string
Content string
}
type Notification struct {
show atomic.Bool
messages chan Message
current Message
duration time.Duration
callback func()
mu sync.RWMutex
}
func NewNotification(d time.Duration, callback func()) *Notification {
p := &Notification{
messages: make(chan Message, 16),
duration: d,
callback: callback,
}
go p.run()
return p
}
func (p *Notification) Layout(gtx C, th *T) D {
if !p.show.Load() {
return D{}
}
p.mu.RLock()
message := p.current
p.mu.RUnlock()
return component.SurfaceStyle{
Theme: th,
ShadowStyle: component.ShadowStyle{
CornerRadius: 8,
},
Fill: theme.Current().NotificationBg,
}.Layout(gtx, func(gtx C) D {
return layout.Inset{
Top: 8,
Bottom: 8,
Left: 8,
Right: 8,
}.Layout(gtx, func(gtx C) D {
return layout.Flex{
Alignment: layout.Middle,
}.Layout(gtx,
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
switch message.Type {
case Success:
return icons.IconInfo.Layout(gtx, color.NRGBA(colornames.Green500))
case Warn:
return icons.IconInfo.Layout(gtx, color.NRGBA(colornames.Orange500))
case Error:
return icons.IconAlert.Layout(gtx, color.NRGBA(colornames.Red500))
case Info:
fallthrough
default:
return icons.IconInfo.Layout(gtx, color.NRGBA(colornames.Blue500))
}
}),
layout.Rigid(layout.Spacer{Width: 8}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return material.Body2(th, message.Content).Layout(gtx)
}),
)
})
})
}
func (p *Notification) Show(message Message) {
select {
case p.messages <- message:
default:
}
}
func (p *Notification) run() {
for m := range p.messages {
p.mu.Lock()
p.current = m
p.mu.Unlock()
p.show.Store(true)
if p.callback != nil {
p.callback()
}
<-time.After(p.duration)
p.show.Store(false)
if p.callback != nil {
p.callback()
}
}
}
+42 -9
View File
@@ -1,13 +1,14 @@
package widget
import (
"strings"
"gioui.org/layout"
"gioui.org/widget"
"gioui.org/widget/material"
"gioui.org/x/component"
"gioui.org/x/outlay"
"github.com/go-gost/gost.plus/ui/i18n"
"github.com/go-gost/gost.plus/ui/icons"
"github.com/go-gost/gost.plus/ui/theme"
)
type SelectorItem struct {
@@ -24,8 +25,8 @@ type Selector struct {
func (p *Selector) Layout(gtx layout.Context, th *material.Theme) layout.Dimensions {
return material.Clickable(gtx, &p.clickable, func(gtx layout.Context) layout.Dimensions {
return layout.Inset{
Top: 8,
Bottom: 8,
Top: 4,
Bottom: 4,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{
Alignment: layout.Middle,
@@ -34,17 +35,49 @@ func (p *Selector) Layout(gtx layout.Context, th *material.Theme) layout.Dimensi
layout.Rigid(layout.Spacer{Width: 8}.Layout),
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return layout.E.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
var names []string
var values []string
for _, item := range p.items {
if item.Name != "" {
names = append(names, item.Name.Value())
if item.Value == "" {
continue
}
value := item.Name.Value()
if value == "" {
value = item.Value
}
values = append(values, value)
}
return material.Body2(th, strings.Join(names, ",")).Layout(gtx)
return outlay.FlowWrap{
Alignment: layout.Middle,
}.Layout(gtx, len(values), func(gtx layout.Context, i int) layout.Dimensions {
return layout.UniformInset(4).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return component.SurfaceStyle{
Theme: th,
ShadowStyle: component.ShadowStyle{CornerRadius: 14},
Fill: theme.Current().ItemBg,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Inset{
Top: 4,
Bottom: 4,
Left: 10,
Right: 10,
}.Layout(gtx, material.Body2(th, values[i]).Layout)
})
})
})
})
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return icons.IconNavRight.Layout(gtx, th.Fg)
if len(p.items) > 0 {
return layout.Dimensions{}
}
return layout.Inset{
Top: 4,
Bottom: 5,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return icons.IconNavRight.Layout(gtx, th.Fg)
})
}),
)
})
+14
View File
@@ -0,0 +1,14 @@
package widget
import (
"gioui.org/layout"
"gioui.org/widget/material"
)
type C = layout.Context
type D = layout.Dimensions
type T = material.Theme
type Widget interface {
Layout(gtx C, th *T) D
}
+1 -1
View File
@@ -1,3 +1,3 @@
package version
var Version = "0.3.1"
var Version = "0.3.2"