add(project): initial commit
This commit is contained in:
parent
f34565bf49
commit
cf17a4ec59
11 changed files with 628 additions and 0 deletions
194
internal/components/navigation_menu.go
Normal file
194
internal/components/navigation_menu.go
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"simple_ui/internal/utils"
|
||||
"time"
|
||||
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget"
|
||||
"gioui.org/widget/material"
|
||||
"golang.org/x/exp/shiny/materialdesign/icons"
|
||||
)
|
||||
|
||||
type NavigationMenu struct {
|
||||
theme *material.Theme
|
||||
contractButton *widget.Clickable
|
||||
items []MenuItem
|
||||
contracted bool
|
||||
selected int
|
||||
anim utils.Animation
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
Label string
|
||||
Button *widget.Clickable
|
||||
Icon *widget.Icon
|
||||
OnClick func()
|
||||
}
|
||||
|
||||
func NewNavigationMenu(theme *material.Theme) *NavigationMenu {
|
||||
nm := &NavigationMenu{
|
||||
theme: theme,
|
||||
contractButton: &widget.Clickable{},
|
||||
items: make([]MenuItem, 0),
|
||||
}
|
||||
nm.anim.Start(0, 0, 0, utils.EasingInOutSine)
|
||||
return nm
|
||||
}
|
||||
|
||||
func (nm *NavigationMenu) ToggleContracted() {
|
||||
nm.contracted = !nm.contracted
|
||||
var end float32
|
||||
if nm.contracted {
|
||||
end = 1
|
||||
} else {
|
||||
end = 0
|
||||
}
|
||||
nm.anim.Start(nm.anim.Current(), end, 250*time.Millisecond, utils.EasingInOutSine)
|
||||
fmt.Printf("Navigation menu toggled: %v\n", nm.contracted)
|
||||
}
|
||||
|
||||
func (nm *NavigationMenu) IsContracted() bool {
|
||||
return nm.contracted
|
||||
}
|
||||
|
||||
func (nm *NavigationMenu) AddItem(label string, icon *widget.Icon, onClick func()) *widget.Clickable {
|
||||
btn := &widget.Clickable{}
|
||||
nm.items = append(nm.items, MenuItem{
|
||||
Label: label,
|
||||
Button: btn,
|
||||
Icon: icon,
|
||||
OnClick: onClick,
|
||||
})
|
||||
return btn
|
||||
}
|
||||
|
||||
func (nm *NavigationMenu) SetSelected(index int) {
|
||||
if index >= 0 && index < len(nm.items) {
|
||||
nm.selected = index
|
||||
}
|
||||
}
|
||||
|
||||
func (nm *NavigationMenu) HandleClicks(gtx layout.Context) {
|
||||
for i, item := range nm.items {
|
||||
if item.Button.Clicked(gtx) {
|
||||
nm.selected = i
|
||||
if item.OnClick != nil {
|
||||
item.OnClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (nm *NavigationMenu) Layout(gtx layout.Context) layout.Dimensions {
|
||||
// Animate contraction/expansion
|
||||
value := nm.anim.Update(gtx)
|
||||
contractedWidth := float32(gtx.Dp(unit.Dp(100)))
|
||||
expandedWidth := float32(gtx.Dp(unit.Dp(240)))
|
||||
width := int(expandedWidth + (contractedWidth-expandedWidth)*value)
|
||||
gtx.Constraints.Min.X = width
|
||||
gtx.Constraints.Max.X = width
|
||||
|
||||
// Add background color
|
||||
backgroundColor := color.NRGBA{R: 205, G: 205, B: 205, A: 255} // Blue
|
||||
return layout.Background{}.Layout(gtx,
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
paint.FillShape(gtx.Ops, backgroundColor, clip.Rect{Max: gtx.Constraints.Min}.Op())
|
||||
return layout.Dimensions{Size: gtx.Constraints.Min}
|
||||
},
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{
|
||||
Axis: layout.Vertical,
|
||||
}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
|
||||
return layout.UniformInset(unit.Dp(8)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{
|
||||
Axis: layout.Horizontal,
|
||||
Alignment: layout.Middle,
|
||||
}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if nm.contracted {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return material.H6(nm.theme, "Simple Weather").Layout(gtx)
|
||||
}),
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Spacer{Width: unit.Dp(16)}.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
contractedIcon, err := widget.NewIcon(icons.NavigationChevronLeft)
|
||||
if err != nil {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
if nm.IsContracted() {
|
||||
contractedIcon, err = widget.NewIcon(icons.NavigationChevronRight)
|
||||
if err != nil {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
}
|
||||
|
||||
if nm.contractButton.Clicked(gtx) {
|
||||
nm.ToggleContracted()
|
||||
}
|
||||
contractButton := material.IconButton(nm.theme, nm.contractButton, contractedIcon, "Toggle menu")
|
||||
contractButton.Background = nm.theme.Palette.Bg
|
||||
contractButton.Color = nm.theme.Palette.Fg
|
||||
|
||||
return contractButton.Layout(gtx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
}),
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{
|
||||
Axis: layout.Vertical,
|
||||
}.Layout(gtx, nm.menuItems()...)
|
||||
}),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (nm *NavigationMenu) menuItems() []layout.FlexChild {
|
||||
children := make([]layout.FlexChild, len(nm.items))
|
||||
|
||||
for i, item := range nm.items {
|
||||
i, item := i, item
|
||||
children[i] = layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
|
||||
var state ButtonState
|
||||
|
||||
if i == nm.selected {
|
||||
state = SelectedButton
|
||||
} else {
|
||||
state = UnselectedButton
|
||||
}
|
||||
axis := layout.Horizontal
|
||||
|
||||
if nm.IsContracted() {
|
||||
axis = layout.Vertical
|
||||
}
|
||||
|
||||
btn := &iconAndTextButton{
|
||||
theme: nm.theme,
|
||||
button: item.Button,
|
||||
icon: item.Icon,
|
||||
word: item.Label,
|
||||
state: state,
|
||||
axis: axis,
|
||||
}
|
||||
|
||||
return layout.Inset{Top: unit.Dp(2), Bottom: unit.Dp(2), Left: unit.Dp(4), Right: unit.Dp(4)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return btn.Layout(gtx)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue