From 99cf3b3843c14c67d7f7bdec03b8ebcb5c418479 Mon Sep 17 00:00:00 2001 From: zervo Date: Fri, 31 Jan 2025 23:20:05 +0100 Subject: [PATCH] Progress --- src/go.mod | 1 + src/go.sum | 2 ++ src/internal/ui/layout.go | 26 ++++---------- src/internal/ui/sidebar.go | 60 ++++++++++++++++++++++++++------ src/internal/ui/styling/theme.go | 17 +++++++++ src/internal/ui/ui.go | 4 +++ src/pkg/layouts/split.go | 38 ++++++++++++++++++++ 7 files changed, 119 insertions(+), 29 deletions(-) create mode 100644 src/internal/ui/styling/theme.go create mode 100644 src/pkg/layouts/split.go diff --git a/src/go.mod b/src/go.mod index 7cd9f5b..4a398f3 100644 --- a/src/go.mod +++ b/src/go.mod @@ -8,6 +8,7 @@ require ( ) require ( + gio.tools/icons v0.0.0-20240708021058-44790e75e701 // indirect gioui.org/shader v1.0.8 // indirect github.com/go-text/typesetting v0.2.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect diff --git a/src/go.sum b/src/go.sum index 0c9dda1..63fc416 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,5 +1,7 @@ eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= +gio.tools/icons v0.0.0-20240708021058-44790e75e701 h1:uLF9lM2XdkhjH2bhWdTEONz9y4nuweF377ice94rNVQ= +gio.tools/icons v0.0.0-20240708021058-44790e75e701/go.mod h1:yHac4ydqWEu6ZgRJ2c2izqy285pdJRHZczqlbhDZIi0= gioui.org v0.8.0 h1:QV5p5JvsmSmGiIXVYOKn6d9YDliTfjtLlVf5J+BZ9Pg= gioui.org v0.8.0/go.mod h1:vEMmpxMOd/iwJhXvGVIzWEbxMWhnMQ9aByOGQdlQ8rc= gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= diff --git a/src/internal/ui/layout.go b/src/internal/ui/layout.go index 6b27017..c8d724b 100644 --- a/src/internal/ui/layout.go +++ b/src/internal/ui/layout.go @@ -1,20 +1,19 @@ package ui import ( + "icdb/internal/ui/styling" + "icdb/pkg/layouts" t_ui "icdb/pkg/types/ui" "gioui.org/app" - "gioui.org/layout" "gioui.org/op" - "gioui.org/unit" - "gioui.org/widget/material" ) var currentView t_ui.UIView = t_ui.UIViewMain // run takes a window as parameter and runs the UI on it. func run(window *app.Window) error { - theme := material.NewTheme() + theme := styling.GlobalTheme() var ops op.Ops for { switch e := window.Event().(type) { @@ -23,21 +22,10 @@ func run(window *app.Window) error { case app.FrameEvent: gtx := app.NewContext(&ops, e) - margins := layout.Inset{ - Top: unit.Dp(10), - Bottom: unit.Dp(10), - Left: unit.Dp(10), - Right: unit.Dp(10), - } - - margins.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - return layout.Flex{ - Axis: layout.Horizontal, - }.Layout(gtx, - layout.Rigid(renderSidebar(theme)), - layout.Flexed(1, renderView(theme)), - ) - }) + layouts.SplitRatio{Ratio: -0.8}.Layout(gtx, + renderSidebar(theme), + renderView(theme), + ) e.Frame(gtx.Ops) } diff --git a/src/internal/ui/sidebar.go b/src/internal/ui/sidebar.go index 1422129..18beb89 100644 --- a/src/internal/ui/sidebar.go +++ b/src/internal/ui/sidebar.go @@ -2,9 +2,13 @@ package ui import ( t_ui "icdb/pkg/types/ui" + "image" "image/color" + "gio.tools/icons" "gioui.org/layout" + "gioui.org/op/clip" + "gioui.org/op/paint" "gioui.org/unit" "gioui.org/widget" "gioui.org/widget/material" @@ -17,27 +21,63 @@ var ( ) var sidebarWidth = unit.Dp(200) -var sidebarBackground = color.NRGBA{R: 240, G: 240, B: 240, A: 255} +var sidebarBackground = color.NRGBA{R: 225, G: 225, B: 225, A: 255} // renderSidebar creates the sidebar UI. +// It renders a colored background and then adds the navigation buttons. func renderSidebar(th *material.Theme) layout.Widget { return func(gtx layout.Context) layout.Dimensions { - return layout.Flex{ - Axis: layout.Vertical, - }.Layout(gtx, - layout.Rigid(renderNavButton(gtx, th, "Main", &mainButton, t_ui.UIViewMain)), - layout.Rigid(renderNavButton(gtx, th, "Parts", &partsButton, t_ui.UIViewParts)), - layout.Rigid(renderNavButton(gtx, th, "Settings", &settingsButton, t_ui.UIViewSettings)), + return layout.Stack{Alignment: layout.Center}.Layout(gtx, + // Expanded: Draw the background to fill the entire left pane. + layout.Expanded(func(gtx layout.Context) layout.Dimensions { + // Use a rectangle that covers the full widget area. + rect := clip.Rect{ + Min: image.Pt(0, 0), + Max: gtx.Constraints.Max, + } + paint.FillShape(gtx.Ops, sidebarBackground, rect.Op()) + return layout.Dimensions{Size: gtx.Constraints.Max} + }), + // Stacked: Lay out the navigation buttons. + layout.Stacked(func(gtx layout.Context) layout.Dimensions { + // Internal padding for the sidebar. + in := layout.Inset{ + Top: unit.Dp(5), + Left: unit.Dp(5), + Right: unit.Dp(5), + Bottom: unit.Dp(5), + } + return in.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{ + Axis: layout.Vertical, + Alignment: layout.Start, + }.Layout(gtx, + layout.Rigid(renderNavButton(th, icons.ActionHome, "Main", &mainButton, t_ui.UIViewMain)), + layout.Rigid(renderNavButton(th, icons.ActionBuild, "Parts", &partsButton, t_ui.UIViewParts)), + layout.Rigid(renderNavButton(th, icons.ActionSettings, "Settings", &settingsButton, t_ui.UIViewSettings)), + ) + }) + }), ) } } -// renderNavButton creates a navigation button -func renderNavButton(gtx layout.Context, th *material.Theme, label string, btn *widget.Clickable, target t_ui.UIView) layout.Widget { +// renderNavButton creates a navigation icon button. +func renderNavButton(th *material.Theme, icon *widget.Icon, label string, btn *widget.Clickable, target t_ui.UIView) layout.Widget { return func(gtx layout.Context) layout.Dimensions { if btn.Clicked(gtx) { currentView = target } - return material.Button(th, btn, label).Layout(gtx) + + // Padding for each button, to add spacing between them. + in := layout.Inset{ + Bottom: unit.Dp(8), + Left: unit.Dp(4), + Right: unit.Dp(4), + } + + return in.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return material.IconButton(th, btn, icon, label).Layout(gtx) + }) } } diff --git a/src/internal/ui/styling/theme.go b/src/internal/ui/styling/theme.go new file mode 100644 index 0000000..332920d --- /dev/null +++ b/src/internal/ui/styling/theme.go @@ -0,0 +1,17 @@ +package styling + +import ( + "image/color" + + "gioui.org/widget/material" + "golang.org/x/image/colornames" +) + +// GlobalTheme returns an instance of the global application theme. +func GlobalTheme() *material.Theme { + theme := material.NewTheme() + theme.Palette.Bg = color.NRGBA(colornames.White) + theme.Palette.Fg = color.NRGBA(colornames.Black) + theme.ContrastBg = color.NRGBA(colornames.Orange) + return theme +} diff --git a/src/internal/ui/ui.go b/src/internal/ui/ui.go index 4d91549..dd162e7 100644 --- a/src/internal/ui/ui.go +++ b/src/internal/ui/ui.go @@ -4,6 +4,7 @@ import ( "os" "gioui.org/app" + "gioui.org/unit" "github.com/rs/zerolog/log" ) @@ -11,6 +12,9 @@ import ( func Init() { go func() { window := new(app.Window) + window.Option(app.Title("ICDB")) + window.Option(app.MinSize(unit.Dp(700), unit.Dp(400))) + window.Option(app.Size(unit.Dp(800), unit.Dp(600))) err := run(window) if err != nil { log.Fatal().Err(err).Msg("UI loop exited with error") diff --git a/src/pkg/layouts/split.go b/src/pkg/layouts/split.go new file mode 100644 index 0000000..9d0b0d8 --- /dev/null +++ b/src/pkg/layouts/split.go @@ -0,0 +1,38 @@ +package layouts + +import ( + "image" + + "gioui.org/layout" + "gioui.org/op" +) + +type SplitRatio struct { + // Ratio keeps the current layout. + // 0 is center, -1 completely to the left, 1 completely to the right. + Ratio float32 +} + +func (s SplitRatio) Layout(gtx layout.Context, left, right layout.Widget) layout.Dimensions { + proportion := (s.Ratio + 1) / 2 + leftsize := int(proportion * float32(gtx.Constraints.Max.X)) + + rightoffset := leftsize + rightsize := gtx.Constraints.Max.X - rightoffset + + { + gtx := gtx + gtx.Constraints = layout.Exact(image.Pt(leftsize, gtx.Constraints.Max.Y)) + left(gtx) + } + + { + trans := op.Offset(image.Pt(rightoffset, 0)).Push(gtx.Ops) + gtx := gtx + gtx.Constraints = layout.Exact(image.Pt(rightsize, gtx.Constraints.Max.Y)) + right(gtx) + trans.Pop() + } + + return layout.Dimensions{Size: gtx.Constraints.Max} +}