622 lines
13 KiB
Go
622 lines
13 KiB
Go
package gel
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"go.uber.org/atomic"
|
|
"golang.org/x/exp/shiny/materialdesign/icons"
|
|
|
|
l "github.com/p9c/p9/pkg/gel/gio/layout"
|
|
"github.com/p9c/p9/pkg/gel/gio/unit"
|
|
)
|
|
|
|
// App defines an application with a header, sidebar/menu, right side button bar, changeable body page widget and
|
|
// pop-over layers
|
|
type App struct {
|
|
*Window
|
|
activePage *atomic.String
|
|
invalidate chan struct{}
|
|
bodyBackground string
|
|
bodyColor string
|
|
pageBackground string
|
|
pageColor string
|
|
cardBackground string
|
|
cardColor string
|
|
buttonBar []l.Widget
|
|
hideSideBar bool
|
|
hideTitleBar bool
|
|
layers []l.Widget
|
|
Logo *[]byte
|
|
LogoClickable *Clickable
|
|
menuBackground string
|
|
menuButton *IconButton
|
|
menuClickable *Clickable
|
|
menuColor string
|
|
menuIcon *[]byte
|
|
MenuOpen bool
|
|
pages WidgetMap
|
|
root *Stack
|
|
sideBar []l.Widget
|
|
sideBarBackground string
|
|
sideBarColor string
|
|
SideBarSize unit.Dp
|
|
sideBarList *List
|
|
Size *atomic.Int32
|
|
statusBar []l.Widget
|
|
statusBarRight []l.Widget
|
|
statusBarBackground string
|
|
statusBarColor string
|
|
title string
|
|
titleBarBackground string
|
|
titleBarColor string
|
|
titleFont string
|
|
mainDirection l.Direction
|
|
PreRendering bool
|
|
Break1 float32
|
|
}
|
|
|
|
type WidgetMap map[string]l.Widget
|
|
|
|
func (w *Window) App(size *atomic.Int32, activePage *atomic.String, Break1 float32, ) *App {
|
|
// mc := w.Clickable()
|
|
a := &App{
|
|
Window: w,
|
|
activePage: activePage,
|
|
bodyBackground: "DocBg",
|
|
bodyColor: "DocText",
|
|
pageBackground: "PanelBg",
|
|
pageColor: "PanelText",
|
|
cardBackground: "DocBg",
|
|
cardColor: "DocText",
|
|
buttonBar: nil,
|
|
hideSideBar: false,
|
|
hideTitleBar: false,
|
|
layers: nil,
|
|
pages: make(WidgetMap),
|
|
root: w.Stack(),
|
|
sideBarBackground: "DocBg",
|
|
sideBarColor: "DocText",
|
|
statusBarBackground: "DocBg",
|
|
statusBarColor: "DocText",
|
|
Logo: &icons.ActionSettingsApplications,
|
|
title: "gio elements application",
|
|
titleBarBackground: "Primary",
|
|
titleBarColor: "DocBg",
|
|
titleFont: "plan9",
|
|
menuIcon: &icons.NavigationMenu,
|
|
menuColor: "DocText",
|
|
MenuOpen: false,
|
|
Size: size,
|
|
mainDirection: l.Center + 1,
|
|
Break1: Break1,
|
|
LogoClickable: w.WidgetPool.GetClickable(),
|
|
sideBarList: w.WidgetPool.GetList(),
|
|
}
|
|
a.SideBarSize = unit.Dp(0)
|
|
return a
|
|
}
|
|
|
|
func (a *App) SetAppTitleText(title string) *App {
|
|
a.title = title
|
|
return a
|
|
}
|
|
|
|
func (a *App) AppTitleText() string {
|
|
return a.title
|
|
}
|
|
|
|
func (a *App) SetLogo(logo *[]byte) *App {
|
|
a.Logo = logo
|
|
return a
|
|
}
|
|
|
|
func (a *App) GetLogo() string {
|
|
return a.title
|
|
}
|
|
|
|
func (a *App) SetMainDirection(direction l.Direction) *App {
|
|
a.mainDirection = direction
|
|
return a
|
|
}
|
|
|
|
func (a *App) MainDirection() l.Direction {
|
|
return a.mainDirection
|
|
}
|
|
|
|
// Fn renders the node widget
|
|
func (a *App) Fn() func(gtx l.Context) l.Dimensions {
|
|
return func(gtx l.Context) l.Dimensions {
|
|
return a.Fill(a.bodyBackground, l.Center, 0, 0,
|
|
a.VFlex().
|
|
Rigid(
|
|
a.RenderHeader,
|
|
).
|
|
Flexed(
|
|
1,
|
|
a.MainFrame,
|
|
).
|
|
Rigid(
|
|
a.RenderStatusBar,
|
|
).
|
|
Fn,
|
|
).
|
|
Fn(gtx)
|
|
}
|
|
}
|
|
|
|
func (a *App) RenderStatusBar(gtx l.Context) l.Dimensions {
|
|
bar := a.Flex()
|
|
for x := range a.statusBar {
|
|
i := x
|
|
bar.Rigid(a.statusBar[i])
|
|
}
|
|
bar.Flexed(1, EmptyMaxWidth())
|
|
for x := range a.statusBarRight {
|
|
i := x
|
|
bar.Rigid(a.statusBarRight[i])
|
|
}
|
|
return bar.Fn(gtx)
|
|
}
|
|
|
|
func (a *App) RenderHeader(gtx l.Context) l.Dimensions {
|
|
return a.Flex().AlignMiddle().
|
|
Rigid(
|
|
a.Theme.Responsive(
|
|
a.Size.Load(),
|
|
Widgets{
|
|
{Widget: If(len(a.sideBar) > 0, a.MenuButton, a.NoMenuButton)},
|
|
{Size: a.Break1, Widget: a.NoMenuButton},
|
|
},
|
|
).
|
|
Fn,
|
|
).
|
|
Flexed(
|
|
1, If(
|
|
float32(a.Width.Load()) >= float32(a.TextSize)*a.Break1,
|
|
a.Direction().W().Embed(a.LogoAndTitle).Fn,
|
|
a.Direction().Center().Embed(a.LogoAndTitle).Fn,
|
|
),
|
|
).
|
|
Rigid(
|
|
a.RenderButtonBar,
|
|
).Fn(gtx)
|
|
}
|
|
|
|
func (a *App) RenderButtonBar(gtx l.Context) l.Dimensions {
|
|
out := a.Theme.Flex()
|
|
for i := range a.buttonBar {
|
|
out.Rigid(a.buttonBar[i])
|
|
}
|
|
dims := out.Fn(gtx)
|
|
gtx.Constraints.Min = dims.Size
|
|
gtx.Constraints.Max = dims.Size
|
|
return dims
|
|
}
|
|
|
|
func (a *App) MainFrame(gtx l.Context) l.Dimensions {
|
|
return a.Flex().
|
|
Rigid(
|
|
a.VFlex().
|
|
Flexed(
|
|
1,
|
|
a.Responsive(
|
|
a.Size.Load(), Widgets{
|
|
{
|
|
Widget: func(gtx l.Context) l.Dimensions {
|
|
return If(
|
|
a.MenuOpen,
|
|
a.renderSideBar(),
|
|
EmptySpace(0, 0),
|
|
)(gtx)
|
|
},
|
|
},
|
|
{
|
|
Size: a.Break1,
|
|
Widget:
|
|
a.renderSideBar(),
|
|
},
|
|
},
|
|
).Fn,
|
|
).Fn,
|
|
).
|
|
Flexed(
|
|
1,
|
|
a.RenderPage,
|
|
).
|
|
Fn(gtx)
|
|
}
|
|
|
|
func (a *App) MenuButton(gtx l.Context) l.Dimensions {
|
|
bg := "Transparent"
|
|
color := a.menuColor
|
|
if a.MenuOpen {
|
|
color = "DocText"
|
|
bg = a.sideBarBackground
|
|
}
|
|
return a.Theme.Flex().SpaceEvenly().AlignEnd().
|
|
Rigid(
|
|
a.ButtonLayout(a.menuClickable).
|
|
CornerRadius(0).
|
|
Embed(
|
|
a.Inset(
|
|
0.375,
|
|
a.Icon().
|
|
Scale(Scales["H5"]).
|
|
Color(color).
|
|
Src(&icons.NavigationMenu).
|
|
Fn,
|
|
).Fn,
|
|
).
|
|
Background(bg).
|
|
SetClick(
|
|
func() {
|
|
a.MenuOpen = !a.MenuOpen
|
|
},
|
|
).
|
|
Fn,
|
|
).Fn(gtx)
|
|
}
|
|
|
|
func (a *App) NoMenuButton(_ l.Context) l.Dimensions {
|
|
a.MenuOpen = false
|
|
return l.Dimensions{}
|
|
}
|
|
|
|
func (a *App) LogoAndTitle(gtx l.Context) l.Dimensions {
|
|
return a.Theme.Responsive(
|
|
a.Size.Load(), Widgets{
|
|
{
|
|
Widget: a.Theme.Flex().AlignMiddle().
|
|
Rigid(
|
|
a.
|
|
Inset(
|
|
0.25, a.
|
|
IconButton(
|
|
a.LogoClickable.
|
|
SetClick(
|
|
func() {
|
|
D.Ln("clicked logo")
|
|
a.Theme.Dark.Flip()
|
|
a.Theme.Colors.SetDarkTheme(a.Theme.Dark.True())
|
|
},
|
|
),
|
|
).
|
|
Icon(
|
|
a.Icon().
|
|
Scale(Scales["H6"]).
|
|
Color("DocText").
|
|
Src(a.Logo),
|
|
).
|
|
Background("Transparent").
|
|
Color("DocText").
|
|
ButtonInset(0.25).
|
|
Corners(0).
|
|
Fn,
|
|
).
|
|
Fn,
|
|
).
|
|
Rigid(
|
|
a.H5(a.ActivePageGet()).
|
|
Color("DocText").Fn,
|
|
).
|
|
Fn,
|
|
},
|
|
{
|
|
Size: a.Break1,
|
|
Widget: a.Theme.Flex().AlignMiddle().
|
|
Rigid(
|
|
a.
|
|
Inset(
|
|
0.25, a.
|
|
IconButton(
|
|
a.LogoClickable.
|
|
SetClick(
|
|
func() {
|
|
D.Ln("clicked logo")
|
|
a.Theme.Dark.Flip()
|
|
a.Theme.Colors.SetDarkTheme(a.Theme.Dark.True())
|
|
},
|
|
),
|
|
).
|
|
Icon(
|
|
a.Icon().
|
|
Scale(Scales["H6"]).
|
|
Color("DocText").
|
|
Src(a.Logo),
|
|
).
|
|
Background("Transparent").Color("DocText").
|
|
ButtonInset(0.25).
|
|
Corners(0).
|
|
Fn,
|
|
).
|
|
Fn,
|
|
).
|
|
Rigid(
|
|
a.H5(a.title).Color("DocText").Fn,
|
|
).
|
|
Fn,
|
|
},
|
|
},
|
|
).Fn(gtx)
|
|
}
|
|
|
|
func (a *App) RenderPage(gtx l.Context) l.Dimensions {
|
|
return a.Fill(
|
|
a.pageBackground, l.Center, 0, 0, a.Inset(
|
|
0.25,
|
|
func(gtx l.Context) l.Dimensions {
|
|
if page, ok := a.pages[a.activePage.Load()]; !ok {
|
|
return a.Flex().
|
|
Flexed(
|
|
1,
|
|
a.VFlex().SpaceEvenly().
|
|
Rigid(
|
|
a.H1("404").Fn,
|
|
).
|
|
Rigid(
|
|
a.Body1("page "+a.activePage.Load()+" not found").Fn,
|
|
).
|
|
Fn,
|
|
).Fn(gtx)
|
|
} else {
|
|
return page(gtx)
|
|
}
|
|
},
|
|
).Fn,
|
|
).Fn(gtx)
|
|
}
|
|
|
|
func (a *App) DimensionCaption(gtx l.Context) l.Dimensions {
|
|
return a.Caption(fmt.Sprintf("%dx%d", gtx.Constraints.Max.X, gtx.Constraints.Max.Y)).Fn(gtx)
|
|
}
|
|
|
|
func (a *App) renderSideBar() l.Widget {
|
|
if len(a.sideBar) > 0 {
|
|
le := func(gtx l.Context, index int) l.Dimensions {
|
|
dims := a.sideBar[index](gtx)
|
|
return dims
|
|
}
|
|
return func(gtx l.Context) l.Dimensions {
|
|
a.PreRendering = true
|
|
gtx1 := CopyContextDimensionsWithMaxAxis(gtx, l.Horizontal)
|
|
// generate the dimensions for all the list elements
|
|
allDims := GetDimensionList(gtx1, len(a.sideBar), le)
|
|
a.PreRendering = false
|
|
max := 0
|
|
for _, i := range allDims {
|
|
if i.Size.X > max {
|
|
max = i.Size.X
|
|
}
|
|
}
|
|
a.SideBarSize = unit.Dp(max)
|
|
gtx.Constraints.Max.X = max
|
|
gtx.Constraints.Min.X = max
|
|
out := a.VFlex().
|
|
Rigid(
|
|
a.sideBarList.
|
|
Length(len(a.sideBar)).
|
|
LeftSide(true).
|
|
Vertical().
|
|
ListElement(le).
|
|
Fn,
|
|
)
|
|
return out.Fn(gtx)
|
|
}
|
|
} else {
|
|
return EmptySpace(0, 0)
|
|
}
|
|
}
|
|
|
|
func (a *App) ActivePage(activePage string) *App {
|
|
a.Invalidate()
|
|
a.activePage.Store(activePage)
|
|
return a
|
|
}
|
|
func (a *App) ActivePageGet() string {
|
|
return a.activePage.Load()
|
|
}
|
|
func (a *App) ActivePageGetAtomic() *atomic.String {
|
|
return a.activePage
|
|
}
|
|
|
|
func (a *App) BodyBackground(bodyBackground string) *App {
|
|
a.bodyBackground = bodyBackground
|
|
return a
|
|
}
|
|
func (a *App) BodyBackgroundGet() string {
|
|
return a.bodyBackground
|
|
}
|
|
|
|
func (a *App) BodyColor(bodyColor string) *App {
|
|
a.bodyColor = bodyColor
|
|
return a
|
|
}
|
|
func (a *App) BodyColorGet() string {
|
|
return a.bodyColor
|
|
}
|
|
|
|
func (a *App) CardBackground(cardBackground string) *App {
|
|
a.cardBackground = cardBackground
|
|
return a
|
|
}
|
|
func (a *App) CardBackgroundGet() string {
|
|
return a.cardBackground
|
|
}
|
|
|
|
func (a *App) CardColor(cardColor string) *App {
|
|
a.cardColor = cardColor
|
|
return a
|
|
}
|
|
func (a *App) CardColorGet() string {
|
|
return a.cardColor
|
|
}
|
|
|
|
func (a *App) ButtonBar(bar []l.Widget) *App {
|
|
a.buttonBar = bar
|
|
return a
|
|
}
|
|
func (a *App) ButtonBarGet() (bar []l.Widget) {
|
|
return a.buttonBar
|
|
}
|
|
|
|
func (a *App) HideSideBar(hideSideBar bool) *App {
|
|
a.hideSideBar = hideSideBar
|
|
return a
|
|
}
|
|
func (a *App) HideSideBarGet() bool {
|
|
return a.hideSideBar
|
|
}
|
|
|
|
func (a *App) HideTitleBar(hideTitleBar bool) *App {
|
|
a.hideTitleBar = hideTitleBar
|
|
return a
|
|
}
|
|
func (a *App) HideTitleBarGet() bool {
|
|
return a.hideTitleBar
|
|
}
|
|
|
|
func (a *App) Layers(widgets []l.Widget) *App {
|
|
a.layers = widgets
|
|
return a
|
|
}
|
|
func (a *App) LayersGet() []l.Widget {
|
|
return a.layers
|
|
}
|
|
|
|
func (a *App) MenuBackground(menuBackground string) *App {
|
|
a.menuBackground = menuBackground
|
|
return a
|
|
}
|
|
func (a *App) MenuBackgroundGet() string {
|
|
return a.menuBackground
|
|
}
|
|
|
|
func (a *App) MenuColor(menuColor string) *App {
|
|
a.menuColor = menuColor
|
|
return a
|
|
}
|
|
func (a *App) MenuColorGet() string {
|
|
return a.menuColor
|
|
}
|
|
|
|
func (a *App) MenuIcon(menuIcon *[]byte) *App {
|
|
a.menuIcon = menuIcon
|
|
return a
|
|
}
|
|
func (a *App) MenuIconGet() *[]byte {
|
|
return a.menuIcon
|
|
}
|
|
|
|
func (a *App) Pages(widgets WidgetMap) *App {
|
|
a.pages = widgets
|
|
return a
|
|
}
|
|
func (a *App) PagesGet() WidgetMap {
|
|
return a.pages
|
|
}
|
|
|
|
func (a *App) Root(root *Stack) *App {
|
|
a.root = root
|
|
return a
|
|
}
|
|
func (a *App) RootGet() *Stack {
|
|
return a.root
|
|
}
|
|
|
|
func (a *App) SideBar(widgets []l.Widget) *App {
|
|
a.sideBar = widgets
|
|
return a
|
|
}
|
|
func (a *App) SideBarBackground(sideBarBackground string) *App {
|
|
a.sideBarBackground = sideBarBackground
|
|
return a
|
|
}
|
|
func (a *App) SideBarBackgroundGet() string {
|
|
return a.sideBarBackground
|
|
}
|
|
|
|
func (a *App) SideBarColor(sideBarColor string) *App {
|
|
a.sideBarColor = sideBarColor
|
|
return a
|
|
}
|
|
func (a *App) SideBarColorGet() string {
|
|
return a.sideBarColor
|
|
}
|
|
|
|
func (a *App) SideBarGet() []l.Widget {
|
|
return a.sideBar
|
|
}
|
|
|
|
func (a *App) StatusBar(bar, barR []l.Widget) *App {
|
|
a.statusBar = bar
|
|
a.statusBarRight = barR
|
|
return a
|
|
}
|
|
func (a *App) StatusBarBackground(statusBarBackground string) *App {
|
|
a.statusBarBackground = statusBarBackground
|
|
return a
|
|
}
|
|
func (a *App) StatusBarBackgroundGet() string {
|
|
return a.statusBarBackground
|
|
}
|
|
|
|
func (a *App) StatusBarColor(statusBarColor string) *App {
|
|
a.statusBarColor = statusBarColor
|
|
return a
|
|
}
|
|
func (a *App) StatusBarColorGet() string {
|
|
return a.statusBarColor
|
|
}
|
|
|
|
func (a *App) StatusBarGet() (bar []l.Widget) {
|
|
return a.statusBar
|
|
}
|
|
func (a *App) Title(title string) *App {
|
|
a.title = title
|
|
return a
|
|
}
|
|
func (a *App) TitleBarBackground(TitleBarBackground string) *App {
|
|
a.bodyBackground = TitleBarBackground
|
|
return a
|
|
}
|
|
func (a *App) TitleBarBackgroundGet() string {
|
|
return a.titleBarBackground
|
|
}
|
|
|
|
func (a *App) TitleBarColor(titleBarColor string) *App {
|
|
a.titleBarColor = titleBarColor
|
|
return a
|
|
}
|
|
func (a *App) TitleBarColorGet() string {
|
|
return a.titleBarColor
|
|
}
|
|
|
|
func (a *App) TitleFont(font string) *App {
|
|
a.titleFont = font
|
|
return a
|
|
}
|
|
func (a *App) TitleFontGet() string {
|
|
return a.titleFont
|
|
}
|
|
func (a *App) TitleGet() string {
|
|
return a.title
|
|
}
|
|
|
|
func (a *App) Placeholder(title string) func(gtx l.Context) l.Dimensions {
|
|
return func(gtx l.Context) l.Dimensions {
|
|
return a.VFlex().
|
|
AlignMiddle().
|
|
SpaceSides().
|
|
Rigid(
|
|
a.Flex().
|
|
Flexed(0.5, EmptyMaxWidth()).
|
|
Rigid(
|
|
a.H1(title).Fn,
|
|
).
|
|
Flexed(0.5, EmptyMaxWidth()).
|
|
Fn,
|
|
).
|
|
Fn(gtx)
|
|
}
|
|
}
|