forked from go/golangs_learn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
3.3 KiB
158 lines
3.3 KiB
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
"log"
|
|
"math"
|
|
"os"
|
|
|
|
"gioui.org/app"
|
|
"gioui.org/f32"
|
|
"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"
|
|
|
|
"gioui.org/font/gofont"
|
|
)
|
|
|
|
func main() {
|
|
go func() {
|
|
defer os.Exit(0)
|
|
w := app.NewWindow()
|
|
if err := loop(w); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}()
|
|
app.Main()
|
|
}
|
|
|
|
func loop(w *app.Window) error {
|
|
th := material.NewTheme(gofont.Collection())
|
|
var ops op.Ops
|
|
for {
|
|
e := <-w.Events()
|
|
switch e := e.(type) {
|
|
case system.DestroyEvent:
|
|
return e.Err
|
|
case system.FrameEvent:
|
|
gtx := layout.NewContext(&ops, e)
|
|
drawTabs(gtx, th)
|
|
e.Frame(gtx.Ops)
|
|
}
|
|
}
|
|
}
|
|
|
|
var tabs Tabs
|
|
var slider Slider
|
|
|
|
type Tabs struct {
|
|
list layout.List
|
|
tabs []Tab
|
|
selected int
|
|
}
|
|
|
|
type Tab struct {
|
|
btn widget.Clickable
|
|
Title string
|
|
}
|
|
|
|
func init() {
|
|
for i := 1; i <= 100; i++ {
|
|
tabs.tabs = append(tabs.tabs,
|
|
Tab{Title: fmt.Sprintf("Tab %d", i)},
|
|
)
|
|
}
|
|
}
|
|
|
|
type (
|
|
C = layout.Context
|
|
D = layout.Dimensions
|
|
)
|
|
|
|
func drawTabs(gtx layout.Context, th *material.Theme) layout.Dimensions {
|
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
|
layout.Rigid(func(gtx C) D {
|
|
return tabs.list.Layout(gtx, len(tabs.tabs), func(gtx C, tabIdx int) D {
|
|
t := &tabs.tabs[tabIdx]
|
|
if t.btn.Clicked() {
|
|
if tabs.selected < tabIdx {
|
|
slider.PushLeft()
|
|
} else if tabs.selected > tabIdx {
|
|
slider.PushRight()
|
|
}
|
|
tabs.selected = tabIdx
|
|
}
|
|
var tabWidth int
|
|
return layout.Stack{Alignment: layout.S}.Layout(gtx,
|
|
layout.Stacked(func(gtx C) D {
|
|
dims := material.Clickable(gtx, &t.btn, func(gtx C) D {
|
|
return layout.UniformInset(unit.Sp(12)).Layout(gtx,
|
|
material.H6(th, t.Title).Layout,
|
|
)
|
|
})
|
|
tabWidth = dims.Size.X
|
|
return dims
|
|
}),
|
|
layout.Stacked(func(gtx C) D {
|
|
if tabs.selected != tabIdx {
|
|
return layout.Dimensions{}
|
|
}
|
|
tabHeight := gtx.Px(unit.Dp(4))
|
|
tabRect := image.Rect(0, 0, tabWidth, tabHeight)
|
|
paint.FillShape(gtx.Ops, th.Palette.ContrastBg, clip.Rect(tabRect).Op())
|
|
return layout.Dimensions{
|
|
Size: image.Point{X: tabWidth, Y: tabHeight},
|
|
}
|
|
}),
|
|
)
|
|
})
|
|
}),
|
|
layout.Flexed(1, func(gtx C) D {
|
|
return slider.Layout(gtx, func(gtx C) D {
|
|
fill(gtx, dynamicColor(tabs.selected), dynamicColor(tabs.selected+1))
|
|
return layout.Center.Layout(gtx,
|
|
material.H1(th, fmt.Sprintf("Tab content #%d", tabs.selected+1)).Layout,
|
|
)
|
|
})
|
|
}),
|
|
)
|
|
}
|
|
|
|
func fill(gtx layout.Context, col1, col2 color.NRGBA) {
|
|
dr := image.Rectangle{Max: gtx.Constraints.Min}
|
|
paint.FillShape(gtx.Ops,
|
|
color.NRGBA{R: 0, G: 0, B: 0, A: 0xFF},
|
|
clip.Rect(dr).Op(),
|
|
)
|
|
|
|
col2.R = byte(float32(col2.R))
|
|
col2.G = byte(float32(col2.G))
|
|
col2.B = byte(float32(col2.B))
|
|
paint.LinearGradientOp{
|
|
Stop1: f32.Pt(float32(dr.Min.X), 0),
|
|
Stop2: f32.Pt(float32(dr.Max.X), 0),
|
|
Color1: col1,
|
|
Color2: col2,
|
|
}.Add(gtx.Ops)
|
|
defer clip.Rect(dr).Push(gtx.Ops).Pop()
|
|
paint.PaintOp{}.Add(gtx.Ops)
|
|
}
|
|
|
|
func dynamicColor(i int) color.NRGBA {
|
|
sn, cs := math.Sincos(float64(i) * math.Phi)
|
|
return color.NRGBA{
|
|
R: 0xA0 + byte(0x30*sn),
|
|
G: 0xA0 + byte(0x30*cs),
|
|
B: 0xD0,
|
|
A: 0xFF,
|
|
}
|
|
}
|
|
|