├── .gitignore ├── LICENSE ├── README.md ├── _example ├── full_theme │ └── full_example.go ├── material-light-inspired.yml ├── simple │ └── simple_example.go └── yaml │ └── yaml_example.go ├── cmd └── prettyfyne │ └── main.go ├── codec.go ├── codec_test.go ├── convert.go ├── doc ├── chooser.png └── preview.png ├── fonts.go ├── go.mod ├── go.sum ├── pallet.go ├── screens ├── color_slider.go ├── easier_src.go ├── fonts.go ├── go_src.go ├── go_src_test.go ├── init.go └── preview.go ├── theme.go └── theme_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Todd Garrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # prettyfyne 2 | 3 | This is a tool to assist with building, and using themes for [fyne](https://github.com/fyne-io/fyne), 4 | a golang graphical user interface library. 5 | 6 | ![prettyfyne Theme Editor](doc/chooser.png) 7 | ![prettyfyne Theme Editor](doc/preview.png) 8 | 9 | Included here are: 10 | 11 | * A graphical interface for changing theme settings, with ability to view yaml and Go source representations. 12 | * Utilities for more easily loading and exchanging themes via yaml files. 13 | * A few example themes. 14 | 15 | This is very early in the development stage, so much is missing like better color pallets, a variety of pre-built themes, 16 | unit tests, actually ... testing of any kind. 17 | 18 | for examples see the [_example](http://github.com/blockpane/prettyfyne/_example) folder. 19 | 20 | ## Usage 21 | 22 | 1. Import Yaml theme from a saved configuration file 23 | 1. override default values to fine-tune an existing theme 24 | 1. create/edit/load entirely new themes 25 | 26 | All three are in _example directory. 27 | 28 | ### Quick example, building a theme from scratch: 29 | 30 | ```golang 31 | package main 32 | 33 | import ( 34 | "fyne.io/fyne" 35 | "fyne.io/fyne/app" 36 | "fyne.io/fyne/theme" 37 | "fyne.io/fyne/widget" 38 | "github.com/blockpane/prettyfyne" 39 | "image/color" 40 | "time" 41 | ) 42 | 43 | func main() { 44 | a := app.New() 45 | w := a.NewWindow("Pretty Fyne - PrettyTheme Example") 46 | 47 | myTheme := prettyfyne.PrettyTheme{ 48 | BackgroundColor: color.RGBA{R: 30, G: 30, B: 30, A: 255}, 49 | ButtonColor: color.RGBA{R: 20, G: 20, B: 20, A: 255}, 50 | DisabledButtonColor: color.RGBA{R: 15, G: 15, B: 17, A: 255}, 51 | HyperlinkColor: color.RGBA{R: 170, G:100, B:20, A: 64}, 52 | TextColor: color.RGBA{R: 200, G: 200, B: 200, A: 255}, 53 | DisabledTextColor: color.RGBA{R: 155, G: 155, B: 155, A: 127}, 54 | IconColor: color.RGBA{R: 150, G: 80, B: 0, A: 255}, 55 | DisabledIconColor: color.RGBA{R: 155, G: 155, B: 155, A: 127}, 56 | PlaceHolderColor: color.RGBA{R: 150, G: 80, B: 0, A: 255}, 57 | PrimaryColor: color.RGBA{R: 110, G: 40, B: 0, A: 127}, 58 | HoverColor: color.RGBA{R: 110, G: 40, B: 0, A: 127}, 59 | FocusColor: color.RGBA{R: 99, G: 99, B: 99, A: 255}, 60 | ScrollBarColor: color.RGBA{R: 35, G: 35, B: 35, A: 8}, 61 | ShadowColor: color.RGBA{R: 0, G: 0, B: 0, A: 64}, 62 | TextSize: 13, 63 | TextFont: theme.DarkTheme().TextFont(), 64 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 65 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 66 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 67 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 68 | Padding: 4, 69 | IconInlineSize: 24, 70 | ScrollBarSize: 10, 71 | ScrollBarSmallSize: 4, 72 | } 73 | 74 | hello := widget.NewLabel("Hello Pretty Fyne!") 75 | 76 | w.SetContent(widget.NewVBox( 77 | hello, 78 | widget.NewButton("Quit", func() { 79 | a.Quit() 80 | }), 81 | )) 82 | 83 | // applying a theme before the window is running will cause a panic, aggressively wait for it to be present 84 | go func(){ 85 | for { 86 | time.Sleep(50 * time.Millisecond) 87 | if hello.Visible() { 88 | break 89 | } 90 | } 91 | fyne.CurrentApp().Settings().SetTheme(myTheme.ToFyneTheme()) 92 | }() 93 | 94 | w.CenterOnScreen() 95 | w.ShowAndRun() 96 | } 97 | ``` -------------------------------------------------------------------------------- /_example/full_theme/full_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fyne.io/fyne" 5 | "fyne.io/fyne/app" 6 | "fyne.io/fyne/theme" 7 | "fyne.io/fyne/widget" 8 | "github.com/blockpane/prettyfyne" 9 | "image/color" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | a := app.New() 15 | w := a.NewWindow("Pretty Fyne - PrettyTheme Example") 16 | 17 | myTheme := prettyfyne.PrettyTheme{ 18 | BackgroundColor: color.RGBA{R: 30, G: 30, B: 30, A: 255}, 19 | ButtonColor: color.RGBA{R: 20, G: 20, B: 20, A: 255}, 20 | DisabledButtonColor: color.RGBA{R: 15, G: 15, B: 17, A: 255}, 21 | HyperlinkColor: color.RGBA{R: 170, G:100, B:20, A: 64}, 22 | TextColor: color.RGBA{R: 200, G: 200, B: 200, A: 255}, 23 | DisabledTextColor: color.RGBA{R: 155, G: 155, B: 155, A: 127}, 24 | IconColor: color.RGBA{R: 150, G: 80, B: 0, A: 255}, 25 | DisabledIconColor: color.RGBA{R: 155, G: 155, B: 155, A: 127}, 26 | PlaceHolderColor: color.RGBA{R: 150, G: 80, B: 0, A: 255}, 27 | PrimaryColor: color.RGBA{R: 110, G: 40, B: 0, A: 127}, 28 | HoverColor: color.RGBA{R: 110, G: 40, B: 0, A: 127}, 29 | FocusColor: color.RGBA{R: 99, G: 99, B: 99, A: 255}, 30 | ScrollBarColor: color.RGBA{R: 35, G: 35, B: 35, A: 8}, 31 | ShadowColor: color.RGBA{R: 0, G: 0, B: 0, A: 64}, 32 | TextSize: 13, 33 | TextFont: theme.DarkTheme().TextFont(), 34 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 35 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 36 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 37 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 38 | Padding: 4, 39 | IconInlineSize: 24, 40 | ScrollBarSize: 10, 41 | ScrollBarSmallSize: 4, 42 | } 43 | 44 | hello := widget.NewLabel("Hello Pretty Fyne!") 45 | 46 | w.SetContent(widget.NewVBox( 47 | hello, 48 | widget.NewButton("Quit", func() { 49 | a.Quit() 50 | }), 51 | )) 52 | 53 | // applying a theme before the window is running will cause a panic, aggressively wait for it to be present 54 | go func(){ 55 | for { 56 | time.Sleep(50 * time.Millisecond) 57 | if hello.Visible() { 58 | break 59 | } 60 | } 61 | fyne.CurrentApp().Settings().SetTheme(myTheme.ToFyneTheme()) 62 | }() 63 | 64 | w.CenterOnScreen() 65 | w.ShowAndRun() 66 | } 67 | -------------------------------------------------------------------------------- /_example/material-light-inspired.yml: -------------------------------------------------------------------------------- 1 | background_color: 2 | r: 255 3 | g: 255 4 | b: 255 5 | a: 255 6 | button_color: 7 | r: 0 8 | g: 0 9 | b: 0 10 | a: 10 11 | disabled_button_color: 12 | r: 0 13 | g: 0 14 | b: 0 15 | a: 30 16 | hyperlink_color: 17 | r: 100 18 | g: 181 19 | b: 246 20 | a: 255 21 | text_color: 22 | r: 0 23 | g: 0 24 | b: 0 25 | a: 222 26 | disabled_text_color: 27 | r: 94 28 | g: 94 29 | b: 94 30 | a: 255 31 | icon_color: 32 | r: 100 33 | g: 181 34 | b: 246 35 | a: 255 36 | disabled_icon_color: 37 | r: 0 38 | g: 0 39 | b: 0 40 | a: 97 41 | place_holder_color: 42 | r: 178 43 | g: 178 44 | b: 178 45 | a: 255 46 | primary_color: 47 | r: 166 48 | g: 212 49 | b: 250 50 | a: 255 51 | hover_color: 52 | r: 0 53 | g: 0 54 | b: 0 55 | a: 30 56 | focus_color: 57 | r: 100 58 | g: 181 59 | b: 246 60 | a: 255 61 | scroll_bar_color: 62 | r: 0 63 | g: 0 64 | b: 0 65 | a: 153 66 | shadow_color: 67 | r: 0 68 | g: 0 69 | b: 0 70 | a: 66 71 | text_size: 13 72 | text_font: NotoSans-Regular.ttf 73 | text_bold_font: NotoSans-Bold.ttf 74 | text_italic_font: NotoSans-Italic.ttf 75 | text_bold_italic_font: NotoSans-BoldItalic.ttf 76 | text_monospace_font: NotoMono-Regular.ttf 77 | padding: 4 78 | icon_inline_size: 24 79 | scroll_bar_size: 16 80 | scroll_bar_small_size: 3 81 | 82 | -------------------------------------------------------------------------------- /_example/simple/simple_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fyne.io/fyne" 5 | "fyne.io/fyne/app" 6 | "fyne.io/fyne/layout" 7 | "fyne.io/fyne/theme" 8 | "fyne.io/fyne/widget" 9 | pf "github.com/blockpane/prettyfyne" 10 | "golang.org/x/image/colornames" 11 | "image/color" 12 | "time" 13 | ) 14 | 15 | func main() { 16 | a := app.New() 17 | w := a.NewWindow("Pretty Fyne - Simple Example") 18 | 19 | // get a copy of the default fyne theme: 20 | prettyTheme := pf.DefaultTheme() 21 | 22 | // Change the font 23 | label := widget.NewLabel("Using custom font Arial.ttf from system path") 24 | var err error 25 | prettyTheme.TextFont, err = pf.LoadFont("", "Arial.ttf", pf.FontDefault) 26 | if err != nil { 27 | label.SetText("Couldn't load custom font, using the default\n"+err.Error()) 28 | } 29 | 30 | // Customize the sizing 31 | prettyTheme.TextSize = 18 32 | prettyTheme.Padding = 5 33 | prettyTheme.IconInlineSize = 24 34 | 35 | // Change a few colors 36 | prettyTheme.TextColor = colornames.Whitesmoke 37 | prettyTheme.DisabledTextColor = colornames.Dimgray 38 | prettyTheme.BackgroundColor = color.RGBA{ R:20, G: 20, B: 20, A: 128} 39 | prettyTheme.ButtonColor = colornames.Black 40 | prettyTheme.HoverColor = colornames.Olivedrab 41 | prettyTheme.IconColor = colornames.Chocolate 42 | prettyTheme.ShadowColor = color.RGBA{ R:10, G: 10, B: 10, A: 255} 43 | 44 | yamlString, _ := prettyTheme.MarshalYaml() 45 | yamlEntry := widget.NewMultiLineEntry() 46 | yamlEntry.SetText(string(yamlString)) 47 | 48 | disabledButton := widget.NewButtonWithIcon("Disabled", theme.InfoIcon(), func(){}) 49 | disabledButton.Disable() 50 | w.SetContent(widget.NewVBox( 51 | widget.NewLabel("Hello Custom Theme!"), 52 | widget.NewHBox( 53 | layout.NewSpacer(), 54 | widget.NewButtonWithIcon("Quit", theme.CancelIcon(), func() { 55 | a.Quit() 56 | }), 57 | disabledButton, 58 | layout.NewSpacer(), 59 | ), 60 | label, 61 | fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(500, 400)), 62 | widget.NewScrollContainer( 63 | yamlEntry, 64 | )), 65 | )) 66 | 67 | go func(){ 68 | w.Hide() 69 | time.Sleep(100 * time.Millisecond) 70 | // call ToFyneTheme to return the interface: 71 | fyne.CurrentApp().Settings().SetTheme(prettyTheme.ToFyneTheme()) 72 | w.Content().Refresh() 73 | w.Show() 74 | }() 75 | 76 | w.SetFixedSize(true) 77 | w.ShowAndRun() 78 | } 79 | -------------------------------------------------------------------------------- /_example/yaml/yaml_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "fyne.io/fyne" 6 | "fyne.io/fyne/app" 7 | "fyne.io/fyne/layout" 8 | "fyne.io/fyne/theme" 9 | "fyne.io/fyne/widget" 10 | "github.com/blockpane/prettyfyne" 11 | "os" 12 | "time" 13 | ) 14 | 15 | // override only a few from the defaults: 16 | var y = ` 17 | background_color: 18 | r: 42 19 | g: 42 20 | b: 42 21 | a: 255 22 | shadow_color: 23 | r: 42 24 | g: 42 25 | b: 42 26 | a: 255 27 | focus_color: 28 | r: 62 29 | g: 62 30 | b: 62 31 | a: 255 32 | hover_color: 33 | r: 0 34 | g: 0 35 | b: 0 36 | a: 255 37 | padding: 8 38 | icon_inline_size: 32 39 | ` 40 | 41 | func main() { 42 | a := app.New() 43 | w := a.NewWindow("Pretty Fyne") 44 | 45 | pt, _, _ := prettyfyne.UnmarshalYaml([]byte(y)) 46 | yamlEntry := widget.NewMultiLineEntry() 47 | yamlEntry.SetText(y) 48 | 49 | w.SetContent(widget.NewVBox( 50 | widget.NewLabel("Theme From Yaml Example"), 51 | widget.NewButtonWithIcon("Quit", theme.CancelIcon(), func() { 52 | a.Quit() 53 | }), 54 | fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(200, 500)), 55 | widget.NewScrollContainer( 56 | yamlEntry, 57 | )), 58 | )) 59 | 60 | go func(){ 61 | // delay for app to be visible: 62 | time.Sleep(100 * time.Millisecond) 63 | fyne.CurrentApp().Settings().SetTheme(pt.ToFyneTheme()) 64 | w.Content().Refresh() 65 | }() 66 | 67 | w.SetFixedSize(true) 68 | w.ShowAndRun() 69 | } 70 | -------------------------------------------------------------------------------- /cmd/prettyfyne/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "fyne.io/fyne" 6 | "fyne.io/fyne/app" 7 | "fyne.io/fyne/dialog" 8 | "fyne.io/fyne/layout" 9 | "fyne.io/fyne/theme" 10 | "fyne.io/fyne/widget" 11 | "github.com/blockpane/prettyfyne" 12 | "github.com/blockpane/prettyfyne/screens" 13 | "gopkg.in/yaml.v2" 14 | "image/color" 15 | "time" 16 | ) 17 | 18 | func main() { 19 | a := app.New() 20 | w := a.NewWindow("Theme Editor") 21 | 22 | pfc := prettyfyne.ExampleEveryDayIsHalloween.ToConfig() 23 | apply := &widget.Button{} 24 | 25 | BackgroundColorChan := make(chan *color.RGBA) 26 | ButtonColorChan := make(chan *color.RGBA) 27 | DisabledButtonColorChan := make(chan *color.RGBA) 28 | HyperlinkColorChan := make(chan *color.RGBA) 29 | TextColorChan := make(chan *color.RGBA) 30 | DisabledTextColorChan := make(chan *color.RGBA) 31 | IconColorChan := make(chan *color.RGBA) 32 | DisabledIconColorChan := make(chan *color.RGBA) 33 | PlaceHolderColorChan := make(chan *color.RGBA) 34 | PrimaryColorChan := make(chan *color.RGBA) 35 | HoverColorChan := make(chan *color.RGBA) 36 | FocusColorChan := make(chan *color.RGBA) 37 | ScrollBarColorChan := make(chan *color.RGBA) 38 | ShadowColorChan := make(chan *color.RGBA) 39 | 40 | go func() { 41 | for { 42 | select { 43 | case c := <- BackgroundColorChan: 44 | pfc.BackgroundColor = c 45 | case c := <- ButtonColorChan: 46 | pfc.ButtonColor = c 47 | case c := <- DisabledButtonColorChan: 48 | pfc.DisabledButtonColor = c 49 | case c := <- HyperlinkColorChan: 50 | pfc.HyperlinkColor = c 51 | case c := <- TextColorChan: 52 | pfc.TextColor = c 53 | case c := <- DisabledTextColorChan: 54 | pfc.DisabledTextColor = c 55 | case c := <- IconColorChan: 56 | pfc.IconColor = c 57 | case c := <- DisabledIconColorChan: 58 | pfc.DisabledIconColor = c 59 | case c := <- PlaceHolderColorChan: 60 | pfc.PlaceHolderColor = c 61 | case c := <- PrimaryColorChan: 62 | pfc.PrimaryColor = c 63 | case c := <- HoverColorChan: 64 | pfc.HoverColor = c 65 | case c := <- FocusColorChan: 66 | pfc.FocusColor = c 67 | case c := <- ScrollBarColorChan: 68 | pfc.ScrollBarColor = c 69 | case c := <- ShadowColorChan: 70 | pfc.ShadowColor = c 71 | } 72 | } 73 | }() 74 | 75 | bgc := screens.ColorSlider("BackgroundColor", pfc.BackgroundColor, BackgroundColorChan) 76 | bc := screens.ColorSlider("ButtonColor", pfc.ButtonColor, ButtonColorChan) 77 | dbc := screens.ColorSlider("DisabledButtonColor", pfc.DisabledButtonColor, DisabledButtonColorChan) 78 | hlc := screens.ColorSlider("HyperlinkColor", pfc.HyperlinkColor, HyperlinkColorChan) 79 | tc := screens.ColorSlider("TextColor", pfc.TextColor, TextColorChan) 80 | dtc := screens.ColorSlider("DisabledTextColor", pfc.DisabledTextColor, DisabledTextColorChan) 81 | ic := screens.ColorSlider("IconColor", pfc.IconColor, IconColorChan) 82 | dic := screens.ColorSlider("DisabledIconColor", pfc.DisabledIconColor, DisabledIconColorChan) 83 | phc := screens.ColorSlider("PlaceHolderColor", pfc.PlaceHolderColor, PlaceHolderColorChan) 84 | pc := screens.ColorSlider("PrimaryColor", pfc.PrimaryColor, PrimaryColorChan) 85 | hc := screens.ColorSlider("HoverColor", pfc.HoverColor, HoverColorChan) 86 | fc := screens.ColorSlider("FocusColor", pfc.FocusColor, FocusColorChan) 87 | sbc := screens.ColorSlider("ScrollBarColor", pfc.ScrollBarColor, ScrollBarColorChan) 88 | sc := screens.ColorSlider("ShadowColor", pfc.ShadowColor, ShadowColorChan) 89 | 90 | toRbga := func(c color.Color) *color.RGBA { 91 | r, g, b, a := c.RGBA() 92 | return &color.RGBA{ 93 | R: uint8(r), 94 | G: uint8(g), 95 | B: uint8(b), 96 | A: uint8(a), 97 | } 98 | } 99 | set := func(t prettyfyne.PrettyTheme) { 100 | BackgroundColorChan <- toRbga(t.BackgroundColor) 101 | ButtonColorChan <- toRbga(t.ButtonColor) 102 | DisabledButtonColorChan <- toRbga(t.DisabledButtonColor) 103 | HyperlinkColorChan <- toRbga(t.HyperlinkColor) 104 | TextColorChan <- toRbga(t.TextColor) 105 | DisabledTextColorChan <- toRbga(t.DisabledTextColor) 106 | IconColorChan <- toRbga(t.IconColor) 107 | DisabledIconColorChan <- toRbga(t.DisabledIconColor) 108 | PlaceHolderColorChan <- toRbga(t.PlaceHolderColor) 109 | PrimaryColorChan <- toRbga(t.PrimaryColor) 110 | HoverColorChan <- toRbga(t.HoverColor) 111 | FocusColorChan <- toRbga(t.FocusColor) 112 | ScrollBarColorChan <- toRbga(t.ScrollBarColor) 113 | ShadowColorChan <- toRbga(t.ShadowColor) 114 | bgc.Objects = screens.ColorSlider("BackgroundColor", toRbga(t.BackgroundColor), BackgroundColorChan).Objects 115 | bc.Objects = screens.ColorSlider("ButtonColor", toRbga(t.ButtonColor), ButtonColorChan).Objects 116 | dbc.Objects = screens.ColorSlider("DisabledButtonColor", toRbga(t.DisabledButtonColor), DisabledButtonColorChan).Objects 117 | hlc.Objects = screens.ColorSlider("HyperlinkColor", toRbga(t.HyperlinkColor), HyperlinkColorChan).Objects 118 | tc.Objects = screens.ColorSlider("TextColor", toRbga(t.TextColor), TextColorChan).Objects 119 | dtc.Objects = screens.ColorSlider("DisabledTextColor", toRbga(t.DisabledTextColor), DisabledTextColorChan).Objects 120 | ic.Objects = screens.ColorSlider("IconColor", toRbga(t.IconColor), IconColorChan).Objects 121 | dic.Objects = screens.ColorSlider("DisabledIconColor", toRbga(t.DisabledIconColor), DisabledIconColorChan).Objects 122 | phc.Objects = screens.ColorSlider("PlaceHolderColor", toRbga(t.PlaceHolderColor), PlaceHolderColorChan).Objects 123 | pc.Objects = screens.ColorSlider("PrimaryColor", toRbga(t.PrimaryColor), PrimaryColorChan).Objects 124 | hc.Objects = screens.ColorSlider("HoverColor", toRbga(t.HoverColor), HoverColorChan).Objects 125 | fc.Objects = screens.ColorSlider("FocusColor", toRbga(t.FocusColor), FocusColorChan).Objects 126 | sbc.Objects = screens.ColorSlider("ScrollBarColor", toRbga(t.ScrollBarColor), ScrollBarColorChan).Objects 127 | sc.Objects = screens.ColorSlider("ShadowColor", toRbga(t.ShadowColor), ShadowColorChan).Objects 128 | //w.Content().Refresh() 129 | apply.Tapped(&fyne.PointEvent{}) 130 | } 131 | w.SetMainMenu(fyne.NewMainMenu( 132 | fyne.NewMenu("Import", fyne.NewMenuItem("Paste Yaml", func() { 133 | go func() { 134 | y := loadPastedYaml() 135 | if y != nil { 136 | set(*y) 137 | } 138 | }() 139 | })), 140 | )) 141 | quickSelect := widget.NewSelect( 142 | []string{ 143 | "Default Theme", 144 | "Default Light Theme", 145 | "Every Day is Halloween", 146 | "Material Light", 147 | "Tree", 148 | "Dracula", 149 | "Cubicle Life", 150 | }, 151 | func(s string) { 152 | switch s { 153 | case "Default Theme": 154 | set(*prettyfyne.DefaultTheme()) 155 | case "Default Light Theme": 156 | set(*prettyfyne.DefaultLightTheme()) 157 | case "Every Day is Halloween": 158 | set(prettyfyne.ExampleEveryDayIsHalloween) 159 | case "Material Light": 160 | set(prettyfyne.ExampleMaterialLight) 161 | case "Tree": 162 | set(prettyfyne.ExampleTree) 163 | case "Dracula": 164 | set(prettyfyne.ExampleDracula) 165 | case "Cubicle Life": 166 | set(prettyfyne.ExampleCubicleLife) 167 | } 168 | }, 169 | ) 170 | 171 | paddingValue := widget.NewLabel(fmt.Sprintf("%d", pfc.Padding)) 172 | paddingSlider := widget.NewSlider(1.0, 32.0) 173 | paddingSlider.Step = 1.0 174 | paddingSlider.OnChanged = func(f float64) { 175 | paddingValue.SetText(fmt.Sprintf("%d", int(f))) 176 | pfc.Padding = int(f) 177 | } 178 | paddingSlider.Value = float64(pfc.Padding) 179 | padding := fyne.NewContainerWithLayout(layout.NewGridLayout(4), 180 | widget.NewLabel("Padding"), 181 | layout.NewSpacer(), 182 | paddingSlider, 183 | paddingValue, 184 | ) 185 | 186 | iconValue := widget.NewLabel(fmt.Sprintf("%d", pfc.IconInlineSize)) 187 | iconSlider := widget.NewSlider(1, 128) 188 | iconSlider.Step = 1 189 | iconSlider.Value = float64(pfc.IconInlineSize) 190 | iconSlider.OnChanged = func(f float64) { 191 | iconValue.SetText(fmt.Sprintf("%d", int(f))) 192 | pfc.IconInlineSize = int(f) 193 | } 194 | icon := fyne.NewContainerWithLayout(layout.NewGridLayout(4), 195 | widget.NewLabel("Icon Size"), 196 | layout.NewSpacer(), 197 | iconSlider, 198 | iconValue, 199 | ) 200 | 201 | textSizeValue := widget.NewLabel(fmt.Sprintf("%d", pfc.TextSize)) 202 | textSizeSlider := widget.NewSlider(1, 64) 203 | textSizeSlider.Step = 1 204 | textSizeSlider.Value = float64(pfc.TextSize) 205 | textSizeSlider.OnChanged = func(f float64) { 206 | textSizeValue.SetText(fmt.Sprintf("%d", int(f))) 207 | pfc.TextSize = int(f) 208 | } 209 | textSize := fyne.NewContainerWithLayout(layout.NewGridLayout(4), 210 | widget.NewLabel("Text Size"), 211 | layout.NewSpacer(), 212 | textSizeSlider, 213 | textSizeValue, 214 | ) 215 | 216 | //TODO: font selection 217 | /* 218 | textFontEntry := widget.NewEntry() 219 | textFontPathEntry := widget.NewEntry() 220 | textBoldFontEntry := widget.NewEntry() 221 | textBoldFontPathEntry := widget.NewEntry() 222 | textItalicFontEntry := widget.NewEntry() 223 | textItalicFontPathEntry := widget.NewEntry() 224 | textBoldItalicFontEntry := widget.NewEntry() 225 | textBoldItalicFontPathEntry := widget.NewEntry() 226 | textMonospaceFontEntry := widget.NewEntry() 227 | textMonospaceFontPathEntry := widget.NewEntry() 228 | */ 229 | 230 | tabSourceText := widget.NewMultiLineEntry() 231 | tabSourceText.SetText("Go source example will show here after applying the new theme") 232 | tabPftSourceText := widget.NewMultiLineEntry() 233 | tabPftSourceText.SetText("Go source example will show here after applying the new theme") 234 | tabYamlText := widget.NewMultiLineEntry() 235 | tabYamlText.SetText("YAML theme config will show here after applying the new theme") 236 | 237 | apply = widget.NewButtonWithIcon("Apply", theme.ConfirmIcon(), func(){ 238 | y, _ := yaml.Marshal(pfc) 239 | pt, _, _ := prettyfyne.UnmarshalYaml(y) 240 | tabYamlText.SetText(string(y)) 241 | tabSourceText.SetText(screens.ToGoSource(pfc)) 242 | tabPftSourceText.SetText(screens.ToEasierGoSource(pfc)) 243 | fyne.CurrentApp().Settings().SetTheme(pt.ToFyneTheme()) 244 | w.Content().Refresh() 245 | }) 246 | 247 | tabChooserContent := widget.NewScrollContainer(widget.NewVBox( 248 | widget.NewHBox( 249 | layout.NewSpacer(), 250 | widget.NewLabel("Pretty Fyne Theme Editor"), 251 | layout.NewSpacer(), 252 | ), 253 | widget.NewHBox( 254 | layout.NewSpacer(), 255 | widget.NewLabel("Load Example:"), 256 | quickSelect, 257 | layout.NewSpacer(), 258 | ), 259 | bgc, 260 | bc, 261 | dbc, 262 | hlc, 263 | tc, 264 | dtc, 265 | ic, 266 | dic, 267 | phc, 268 | pc, 269 | hc, 270 | fc, 271 | sbc, 272 | sc, 273 | padding, 274 | icon, 275 | textSize, 276 | widget.NewHBox(layout.NewSpacer(), apply, widget.NewButtonWithIcon("Exit", theme.CancelIcon(), func() {a.Quit()}), layout.NewSpacer()), 277 | )) 278 | tabChooser := widget.NewTabItem("Settings", tabChooserContent) 279 | 280 | tabYamlContent := fyne.NewContainerWithLayout(layout.NewMaxLayout(), 281 | widget.NewScrollContainer(tabYamlText), 282 | ) 283 | tabYaml := widget.NewTabItem("Generated Yaml", tabYamlContent) 284 | 285 | tabSourceContent := fyne.NewContainerWithLayout(layout.NewMaxLayout(), 286 | widget.NewScrollContainer(tabSourceText), 287 | ) 288 | tabSource := widget.NewTabItem("Generated Go Source (Native Fyne Theme)", tabSourceContent) 289 | 290 | tabPftSourceContent := fyne.NewContainerWithLayout(layout.NewMaxLayout(), 291 | widget.NewScrollContainer(tabPftSourceText), 292 | ) 293 | tabPftSource := widget.NewTabItem("Generated Go Source (Pretty Fyne Theme)", tabPftSourceContent) 294 | 295 | tabPreview := widget.NewTabItem("Preview Widgets", screens.Preview()) 296 | 297 | tabs := widget.NewTabContainer(tabChooser, tabPreview, tabYaml, tabSource, tabPftSource) 298 | w.SetContent( 299 | fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(1000, 1000)), 300 | tabs, 301 | )) 302 | go func(){ 303 | for { 304 | time.Sleep(20 * time.Millisecond) 305 | if tabs.Visible() { 306 | break 307 | } 308 | } 309 | fyne.CurrentApp().Settings().SetTheme(prettyfyne.ExampleEveryDayIsHalloween.ToFyneTheme()) 310 | }() 311 | w.CenterOnScreen() 312 | w.ShowAndRun() 313 | } 314 | 315 | func loadPastedYaml() *prettyfyne.PrettyTheme { 316 | themeChan := make(chan *prettyfyne.PrettyTheme) 317 | cancelChan := make(chan bool) 318 | pop := &widget.PopUp{} 319 | winSize := fyne.CurrentApp().Driver().AllWindows()[0].Canvas().Size() 320 | in := widget.NewMultiLineEntry() 321 | cancel := widget.NewButtonWithIcon("Cancel", theme.CancelIcon(), func() { 322 | cancelChan <- true 323 | pop.Hide() 324 | }) 325 | apply := widget.NewButtonWithIcon("Load", theme.ConfirmIcon(), func() { 326 | pt, _, err := prettyfyne.UnmarshalYaml([]byte(in.Text)) 327 | if err != nil { 328 | go dialog.ShowError(err, fyne.CurrentApp().Driver().AllWindows()[0]) 329 | return 330 | } 331 | pop.Hide() 332 | themeChan <- pt 333 | }) 334 | 335 | pop = widget.NewPopUp( 336 | fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(winSize.Width,winSize.Height)), 337 | widget.NewVBox( 338 | fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(winSize.Width,winSize.Height-50)), 339 | widget.NewScrollContainer(in), 340 | ), 341 | widget.NewHBox(layout.NewSpacer(), cancel, apply, layout.NewSpacer()), 342 | ), 343 | ), 344 | fyne.CurrentApp().Driver().AllWindows()[0].Canvas(), 345 | ) 346 | 347 | pop.Show() 348 | for { 349 | select { 350 | case <-cancelChan: 351 | return nil 352 | case t := <-themeChan: 353 | return t 354 | } 355 | } 356 | 357 | } 358 | -------------------------------------------------------------------------------- /codec.go: -------------------------------------------------------------------------------- 1 | package prettyfyne 2 | 3 | import ( 4 | "gopkg.in/yaml.v2" 5 | "image/color" 6 | ) 7 | 8 | // PrettyThemeConfig is used for serialization and loading from yaml 9 | type PrettyThemeConfig struct { 10 | BackgroundColor *color.RGBA `yaml:"background_color,omitempty"` 11 | ButtonColor *color.RGBA `yaml:"button_color,omitempty"` 12 | DisabledButtonColor *color.RGBA `yaml:"disabled_button_color,omitempty"` 13 | HyperlinkColor *color.RGBA `yaml:"hyperlink_color,omitempty"` 14 | TextColor *color.RGBA `yaml:"text_color,omitempty"` 15 | DisabledTextColor *color.RGBA `yaml:"disabled_text_color,omitempty"` 16 | IconColor *color.RGBA `yaml:"icon_color,omitempty"` 17 | DisabledIconColor *color.RGBA `yaml:"disabled_icon_color,omitempty"` 18 | PlaceHolderColor *color.RGBA `yaml:"place_holder_color,omitempty"` 19 | PrimaryColor *color.RGBA `yaml:"primary_color,omitempty"` 20 | HoverColor *color.RGBA `yaml:"hover_color,omitempty"` 21 | FocusColor *color.RGBA `yaml:"focus_color,omitempty"` 22 | ScrollBarColor *color.RGBA `yaml:"scroll_bar_color,omitempty"` 23 | ShadowColor *color.RGBA `yaml:"shadow_color,omitempty"` 24 | TextSize int `yaml:"text_size,omitempty"` 25 | TextFontPath string `yaml:"text_font_path,omitempty"` 26 | TextFont string `yaml:"text_font,omitempty"` 27 | TextBoldFontPath string `yaml:"text_bold_font_path,omitempty"` 28 | TextBoldFont string `yaml:"text_bold_font,omitempty"` 29 | TextItalicFontPath string `yaml:"text_italic_font_path,omitempty"` 30 | TextItalicFont string `yaml:"text_italic_font,omitempty"` 31 | TextBoldItalicFontPath string `yaml:"text_bold_italic_font_path,omitempty"` 32 | TextBoldItalicFont string `yaml:"text_bold_italic_font,omitempty"` 33 | TextMonospaceFontPath string `yaml:"text_monospace_font_path,omitempty"` 34 | TextMonospaceFont string `yaml:"text_monospace_font,omitempty"` 35 | Padding int `yaml:"padding,omitempty"` 36 | IconInlineSize int `yaml:"icon_inline_size,omitempty"` 37 | ScrollBarSize int `yaml:"scroll_bar_size,omitempty"` 38 | ScrollBarSmallSize int `yaml:"scroll_bar_small_size,omitempty"` 39 | } 40 | 41 | // UnmarshalYaml will override the default theme settings with what is stored in a yaml file. All fields are optional 42 | // and will fall back to the default if not set. It will always return a populated theme, even if it cannot load fonts. 43 | func UnmarshalYaml(y []byte) (pt *PrettyTheme, fontsLoaded bool, err error) { 44 | pt = DefaultTheme() 45 | c := PrettyThemeConfig{} 46 | err = yaml.Unmarshal(y, &c) 47 | if err != nil { 48 | return 49 | } 50 | fontsLoaded = true 51 | //switch { 52 | if c.BackgroundColor != nil { 53 | pt.BackgroundColor = c.BackgroundColor 54 | } 55 | if c.ButtonColor != nil { 56 | pt.ButtonColor = c.ButtonColor 57 | } 58 | if c.DisabledButtonColor != nil { 59 | pt.DisabledButtonColor = c.DisabledButtonColor 60 | } 61 | if c.HyperlinkColor != nil { 62 | pt.HyperlinkColor = c.HyperlinkColor 63 | } 64 | if c.TextColor != nil { 65 | pt.TextColor = c.TextColor 66 | } 67 | if c.DisabledTextColor != nil { 68 | pt.DisabledTextColor = c.DisabledTextColor 69 | } 70 | if c.IconColor != nil { 71 | pt.IconColor = c.IconColor 72 | } 73 | if c.DisabledIconColor != nil { 74 | pt.DisabledIconColor = c.DisabledIconColor 75 | } 76 | if c.PlaceHolderColor != nil { 77 | pt.PlaceHolderColor = c.PlaceHolderColor 78 | } 79 | if c.PrimaryColor != nil { 80 | pt.PrimaryColor = c.PrimaryColor 81 | } 82 | if c.HoverColor != nil { 83 | pt.HoverColor = c.HoverColor 84 | } 85 | if c.FocusColor != nil { 86 | pt.FocusColor = c.FocusColor 87 | } 88 | if c.ScrollBarColor != nil { 89 | pt.ScrollBarColor = c.ScrollBarColor 90 | } 91 | if c.ShadowColor != nil { 92 | pt.ShadowColor = c.ShadowColor 93 | } 94 | if c.TextSize != 0 { 95 | pt.TextSize = c.TextSize 96 | } 97 | if c.Padding != 0 { 98 | pt.Padding = c.Padding 99 | } 100 | if c.IconInlineSize != 0 { 101 | pt.IconInlineSize = c.IconInlineSize 102 | } 103 | if c.ScrollBarSize != 0 { 104 | pt.ScrollBarSize = c.ScrollBarSize 105 | } 106 | if c.ScrollBarSmallSize != 0 { 107 | pt.ScrollBarSmallSize = c.ScrollBarSmallSize 108 | } 109 | // handle loading of fonts 110 | if c.TextFontPath != "" || c.TextFont != "" { 111 | pt.TextFont, err = LoadFont(c.TextFontPath, c.TextFont, "NotoSans-Regular.ttf") 112 | if err != nil { 113 | fontsLoaded = false 114 | } 115 | } 116 | if c.TextBoldFontPath != "" || c.TextBoldFont != "" { 117 | pt.TextBoldFont, err = LoadFont(c.TextBoldFontPath, c.TextBoldFont, "NotoSans-Bold.ttf") 118 | if err != nil { 119 | fontsLoaded = false 120 | } 121 | } 122 | if c.TextBoldItalicFontPath != "" || c.TextBoldItalicFont != "" { 123 | pt.TextBoldItalicFont, err = LoadFont(c.TextBoldItalicFontPath, c.TextBoldItalicFont, "NotoSans-BoldItalic.ttf") 124 | if err != nil { 125 | fontsLoaded = false 126 | } 127 | } 128 | if c.TextItalicFontPath != "" || c.TextItalicFont != "" { 129 | pt.TextItalicFont, err = LoadFont(c.TextItalicFontPath, c.TextItalicFont, "NotoSans-Italic.ttf") 130 | if err != nil { 131 | fontsLoaded = false 132 | } 133 | } 134 | if c.TextMonospaceFontPath != "" || c.TextMonospaceFont != "" { 135 | pt.TextMonospaceFont, err = LoadFont(c.TextMonospaceFontPath, c.TextMonospaceFont, "NotoMono-Regular.ttf") 136 | if err != nil { 137 | fontsLoaded = false 138 | } 139 | } 140 | return pt, fontsLoaded, nil 141 | } 142 | -------------------------------------------------------------------------------- /codec_test.go: -------------------------------------------------------------------------------- 1 | package prettyfyne 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | ) 7 | 8 | func TestGetFonts(t *testing.T) { 9 | if runtime.GOOS == "darwin" { 10 | if _, e := GetFonts(); e != nil { 11 | t.Error(e) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /convert.go: -------------------------------------------------------------------------------- 1 | package prettyfyne 2 | 3 | import ( 4 | "fyne.io/fyne" 5 | "image/color" 6 | ) 7 | 8 | // customTheme wraps a PrettyTheme and provides the fyne.Theme interface 9 | type customTheme struct { 10 | pt *PrettyTheme 11 | } 12 | 13 | func (c customTheme) BackgroundColor() color.Color { 14 | return c.pt.BackgroundColor 15 | } 16 | 17 | func (c customTheme) ButtonColor() color.Color { 18 | return c.pt.ButtonColor 19 | } 20 | 21 | func (c customTheme) DisabledButtonColor() color.Color { 22 | return c.pt.DisabledButtonColor 23 | } 24 | 25 | func (c customTheme) HyperlinkColor() color.Color { 26 | return c.pt.HyperlinkColor 27 | } 28 | 29 | func (c customTheme) TextColor() color.Color { 30 | return c.pt.TextColor 31 | } 32 | 33 | func (c customTheme) DisabledTextColor() color.Color { 34 | return c.pt.DisabledTextColor 35 | } 36 | 37 | func (c customTheme) IconColor() color.Color { 38 | return c.pt.IconColor 39 | } 40 | 41 | func (c customTheme) DisabledIconColor() color.Color { 42 | return c.pt.DisabledIconColor 43 | } 44 | 45 | func (c customTheme) PlaceHolderColor() color.Color { 46 | return c.pt.PlaceHolderColor 47 | } 48 | 49 | func (c customTheme) PrimaryColor() color.Color { 50 | return c.pt.PrimaryColor 51 | } 52 | 53 | func (c customTheme) HoverColor() color.Color { 54 | return c.pt.HoverColor 55 | } 56 | 57 | func (c customTheme) FocusColor() color.Color { 58 | return c.pt.FocusColor 59 | } 60 | 61 | func (c customTheme) ScrollBarColor() color.Color { 62 | return c.pt.ScrollBarColor 63 | } 64 | 65 | func (c customTheme) ShadowColor() color.Color { 66 | return c.pt.ShadowColor 67 | } 68 | 69 | func (c customTheme) TextSize() int { 70 | return c.pt.TextSize 71 | } 72 | 73 | func (c customTheme) TextFont() fyne.Resource { 74 | return c.pt.TextFont 75 | } 76 | 77 | func (c customTheme) TextBoldFont() fyne.Resource { 78 | return c.pt.TextBoldFont 79 | } 80 | 81 | func (c customTheme) TextItalicFont() fyne.Resource { 82 | return c.pt.TextItalicFont 83 | } 84 | 85 | func (c customTheme) TextBoldItalicFont() fyne.Resource { 86 | return c.pt.TextBoldItalicFont 87 | } 88 | 89 | func (c customTheme) TextMonospaceFont() fyne.Resource { 90 | return c.pt.TextMonospaceFont 91 | } 92 | 93 | func (c customTheme) Padding() int { 94 | return c.pt.Padding 95 | } 96 | 97 | func (c customTheme) IconInlineSize() int { 98 | return c.pt.IconInlineSize 99 | } 100 | 101 | func (c customTheme) ScrollBarSize() int { 102 | return c.pt.ScrollBarSize 103 | } 104 | 105 | func (c customTheme) ScrollBarSmallSize() int { 106 | return c.pt.ScrollBarSmallSize 107 | } 108 | -------------------------------------------------------------------------------- /doc/chooser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockpane/prettyfyne/4f2d209f9b4335030ad349b960ad5f70ec7c255d/doc/chooser.png -------------------------------------------------------------------------------- /doc/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blockpane/prettyfyne/4f2d209f9b4335030ad349b960ad5f70ec7c255d/doc/preview.png -------------------------------------------------------------------------------- /fonts.go: -------------------------------------------------------------------------------- 1 | package prettyfyne 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fyne.io/fyne" 7 | "fyne.io/fyne/theme" 8 | "os" 9 | "os/user" 10 | "regexp" 11 | "runtime" 12 | "strings" 13 | ) 14 | 15 | type Font string 16 | 17 | const ( 18 | FontDefault Font = "NotoSans-Regular.ttf" 19 | FontBold Font = "NotoSans-Bold.ttf" 20 | FontItalic Font = "NotoSans-Italic.ttf" 21 | FontBoldItalic Font = "NotoSans-BoldItalic.ttf" 22 | FontMono Font = "NotoMono-Regular.ttf" 23 | ) 24 | 25 | // LoadFont gives a best-effort attempt at loading a font, and returns the default font if it fails 26 | func LoadFont(path string, name string, fallbackFont Font) (font fyne.Resource, err error) { 27 | if fallbackFont == "" { 28 | fallbackFont = FontDefault 29 | } 30 | 31 | getFallback := func(name Font) fyne.Resource { 32 | switch name { 33 | case FontDefault: 34 | return theme.DefaultTextFont() 35 | case FontBold: 36 | return theme.DefaultTextBoldFont() 37 | case FontBoldItalic: 38 | return theme.DefaultTextBoldItalicFont() 39 | case FontItalic: 40 | return theme.DefaultTextItalicFont() 41 | case FontMono: 42 | return theme.DefaultTextMonospaceFont() 43 | } 44 | return nil 45 | } 46 | 47 | if path == "" && name == "" { 48 | return getFallback(fallbackFont), errors.New("no font name or path provided") 49 | } 50 | // quick check if it's one of the default fyne theme fonts 51 | font = getFallback(Font(name)) 52 | if font != nil { 53 | return 54 | } 55 | 56 | // see if it's a system font 57 | if path == "" { 58 | sysFonts, err := GetFonts() 59 | if err != nil { 60 | return getFallback(fallbackFont), err 61 | } 62 | 63 | // a few locations to look on mac: 64 | if sysFonts[name] == "/Library/Fonts" { 65 | if _, err := os.Lstat( "/System/Library/Fonts/Supplemental/"+name); err == nil { 66 | path = "/System/Library/Fonts/Supplemental" 67 | } 68 | } else if _, err := os.Lstat( "/System/Library/Fonts/"+name); err == nil { 69 | path = "/System/Library/Fonts" 70 | } else { 71 | path = sysFonts[name] 72 | } 73 | if path == "" { 74 | return getFallback(fallbackFont), errors.New("could not find requested font, not in known fonts path") 75 | } 76 | } 77 | fullPath, _ := regexp.MatchString(`(?i)tt[fc]`, path) // allow putting full path to font in path field 78 | if !strings.HasSuffix(path, string(os.PathSeparator)) && !fullPath { 79 | path = path + string(os.PathSeparator) 80 | } 81 | font, err = fyne.LoadResourceFromPath(path + name) 82 | if err != nil { 83 | return getFallback(fallbackFont), err 84 | } 85 | if font != nil { 86 | return 87 | } 88 | return getFallback(fallbackFont), errors.New("could not load font") 89 | } 90 | 91 | // GetFonts provides a map containing a list of fonts and their location based on system defaults, 92 | // TODO: for now only works on MacOS 93 | func GetFonts() (fonts map[string]string, err error) { 94 | fonts = make(map[string]string) 95 | switch runtime.GOOS { 96 | case "darwin": 97 | defaultFonts := []string{"/opt/X11/share/fonts/TTF/fonts.list", "/Library/Fonts/fonts.list"} 98 | if u, e := user.Current(); e == nil && u.HomeDir != "" { 99 | defaultFonts = append(defaultFonts, u.HomeDir+"/Library/Fonts/fonts.list") 100 | } 101 | for _, path := range defaultFonts { 102 | f, err := os.Open(path) 103 | if err == nil { 104 | func() { 105 | defer f.Close() 106 | reader := bufio.NewReader(f) 107 | for { 108 | line, err := reader.ReadString('\n') 109 | if err != nil { 110 | return 111 | } 112 | if match, _ := regexp.MatchString(`(?i)tt[fc]`, line); match { 113 | entry := strings.Split(line, "//") 114 | if len(entry) == 2 { 115 | fonts[strings.TrimSpace(entry[1])] = entry[0] 116 | } 117 | } 118 | } 119 | }() 120 | } 121 | } 122 | if len(fonts) == 0 { 123 | return fonts, errors.New("none found") 124 | } 125 | return 126 | } 127 | return fonts, errors.New("don't know the default fonts path for " + runtime.GOOS) 128 | } 129 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blockpane/prettyfyne 2 | 3 | go 1.14 4 | 5 | require ( 6 | fyne.io/fyne v1.2.3 7 | golang.org/x/image v0.0.0-20200119044424-58c23975cae1 8 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect 9 | golang.org/x/text v0.3.2 10 | gopkg.in/yaml.v2 v2.2.8 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | fyne.io/fyne v1.2.3 h1:5xwtSBNjxxmg+GF/lYvvf4xPzyjgWQoJVrzb+bt5gaA= 2 | fyne.io/fyne v1.2.3/go.mod h1:JhDdBrPP/Kdr1H5ZT3HW8E/6zlz+GkOldWqSirGBDnY= 3 | github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I= 4 | github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= 5 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 8 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 9 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw= 10 | github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= 11 | github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM= 12 | github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 13 | github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8= 14 | github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw= 15 | github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc= 16 | github.com/josephspurrier/goversioninfo v0.0.0-20190124120936-8611f5a5ff3f/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE= 17 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= 18 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 19 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 20 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 21 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 22 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 23 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 24 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 25 | github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e h1:LJUrNHytcMXWKxnULIHPe5SCb1jDpO9o672VB1x2EuQ= 26 | github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= 27 | github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e h1:FFotfUvew9Eg02LYRl8YybAnm0HCwjjfY5JlOI1oB00= 28 | github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU= 29 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 30 | github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709 h1:Ko2LQMrRU+Oy/+EDBwX7eZ2jp3C47eDBB8EIhKTun+I= 31 | github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 32 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 33 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg= 34 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 35 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 36 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 37 | golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg= 38 | golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 39 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= 40 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 41 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 42 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 43 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 44 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= 46 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 47 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= 48 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 49 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 50 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 51 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 52 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 53 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 54 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 55 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 56 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 57 | -------------------------------------------------------------------------------- /pallet.go: -------------------------------------------------------------------------------- 1 | package prettyfyne 2 | 3 | import ( 4 | "golang.org/x/image/colornames" 5 | "image/color" 6 | "image/color/palette" 7 | "math" 8 | ) 9 | 10 | func PalletMap() map[string][]color.Color { 11 | p := make(map[string][]color.Color) 12 | p["Default"] = []color.Color{color.RGBA{R:0, G:0, B:0, A:0 }} // gets overridden by current theme value in widget 13 | p["Material Light"] = append(MaterialLight, MaterialTypographyLight...) 14 | p["Web Safe"] = palette.WebSafe 15 | p["Plan9"] = palette.Plan9 16 | p["Greyscale"] = makeGrey() 17 | p["SVG 1.1"] = makeSvgColors() 18 | return p 19 | } 20 | 21 | func makeGrey() []color.Color { 22 | c := make([]color.Color, 0) 23 | for i := uint8(0); i <= math.MaxUint8-1; i++ { 24 | c = append(c, color.RGBA{ 25 | R: i, 26 | G: i, 27 | B: i, 28 | A: 255, 29 | }) 30 | } 31 | return c 32 | } 33 | 34 | func makeSvgColors() []color.Color { 35 | c := make([]color.Color, 0) 36 | for _, v := range colornames.Names { 37 | c = append(c, colornames.Map[v]) 38 | } 39 | return c 40 | } 41 | 42 | var MaterialLight = []color.Color{ 43 | color.RGBA{0xa6, 0xd4, 0xfa, 0xff}, 44 | color.RGBA{0xf6, 0xa5, 0xc0, 0xff}, 45 | color.RGBA{0xe5, 0x73, 0x73, 0xff}, 46 | color.RGBA{0xff, 0xb7, 0x4d, 0xff}, 47 | color.RGBA{0x64, 0xb5, 0xf6, 0xff}, 48 | color.RGBA{0x81,0xc7,0x84, 0xff}, 49 | } 50 | 51 | var MaterialTypographyLight = []color.Color { 52 | color.RGBA{0, 0, 0, 222}, 53 | color.RGBA{0, 0, 0, 138}, 54 | color.RGBA{0, 0, 0, 66}, 55 | color.RGBA{0xfa, 0xfa, 0xfa, 0xff}, 56 | color.RGBA{0, 0, 0, 30}, 57 | color.RGBA{0, 0, 0, 10}, 58 | color.RGBA{0, 0, 0, 97}, 59 | color.RGBA{0xff, 0xff, 0xff, 0xff}, 60 | } -------------------------------------------------------------------------------- /screens/color_slider.go: -------------------------------------------------------------------------------- 1 | package screens 2 | 3 | import ( 4 | "fmt" 5 | "fyne.io/fyne" 6 | "fyne.io/fyne/canvas" 7 | "fyne.io/fyne/layout" 8 | "fyne.io/fyne/widget" 9 | "github.com/blockpane/prettyfyne" 10 | "image" 11 | "image/color" 12 | "sort" 13 | "strconv" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | func ColorSlider(label string, rgba *color.RGBA, newColor chan *color.RGBA) *fyne.Container { 19 | // closure to update rgba pointer 20 | r := func(c color.Color) { 21 | r, g, b, a := c.RGBA() 22 | rgba = &color.RGBA{ 23 | R: uint8(r), 24 | G: uint8(g), 25 | B: uint8(b), 26 | A: uint8(a), 27 | } 28 | } 29 | // closure to dereference so we have a copy of original to revert to 30 | oldRgba := func(c *color.RGBA) color.RGBA { 31 | return *c 32 | }(rgba) 33 | // closure to copy the dereferenced original back 34 | resetRgba := func() *color.RGBA { 35 | c := oldRgba 36 | return &c 37 | } 38 | 39 | updated := func() { 40 | newColor <- rgba 41 | } 42 | 43 | palletMap := prettyfyne.PalletMap() 44 | palletMap["Default"] = []color.Color{rgba} 45 | colorPallet := palletMap["Default"] 46 | pallets := func() []string { 47 | s := []string{"Values"} 48 | for p := range palletMap { 49 | s = append(s, p) 50 | } 51 | sort.Strings(s) 52 | return s 53 | }() 54 | 55 | 56 | spacer := layout.NewSpacer() 57 | sampleColor := canvas.NewImageFromImage(imageRgba(rgba, 36, 36)) 58 | 59 | rEntry := widget.NewEntry() 60 | rMux := sync.Mutex{} 61 | rEntry.OnChanged = func(s string) { 62 | rgba.R = checkRgbaInput(s) 63 | go func() { 64 | rMux.Lock() 65 | time.Sleep(300 * time.Millisecond) 66 | if rgba.R == 0 && rEntry.Text != "0" { 67 | rEntry.SetText("0") 68 | } 69 | rMux.Unlock() 70 | }() 71 | sampleColor.Image = imageRgba(rgba , 36, 36) 72 | sampleColor.Refresh() 73 | updated() 74 | } 75 | 76 | gEntry := widget.NewEntry() 77 | gMux := sync.Mutex{} 78 | gEntry.OnChanged = func(s string) { 79 | rgba.G = checkRgbaInput(s) 80 | go func() { 81 | gMux.Lock() 82 | time.Sleep(300 * time.Millisecond) 83 | if rgba.G == 0 && gEntry.Text != "0" { 84 | gEntry.SetText("0") 85 | } 86 | gMux.Unlock() 87 | }() 88 | sampleColor.Image = imageRgba(rgba , 36, 36) 89 | sampleColor.Refresh() 90 | updated() 91 | } 92 | 93 | bEntry := widget.NewEntry() 94 | bMux := sync.Mutex{} 95 | bEntry.OnChanged = func(s string) { 96 | rgba.B = checkRgbaInput(s) 97 | go func() { 98 | bMux.Lock() 99 | time.Sleep(300 * time.Millisecond) 100 | if rgba.B == 0 && bEntry.Text != "0" { 101 | bEntry.SetText("0") 102 | } 103 | bMux.Unlock() 104 | }() 105 | sampleColor.Image = imageRgba(rgba , 36, 36) 106 | sampleColor.Refresh() 107 | updated() 108 | } 109 | 110 | aEntry := widget.NewEntry() 111 | aMux := sync.Mutex{} 112 | aEntry.OnChanged = func(s string) { 113 | rgba.A = checkRgbaInput(s) 114 | go func() { 115 | aMux.Lock() 116 | time.Sleep(300 * time.Millisecond) 117 | if rgba.A == 0 && aEntry.Text != "0" { 118 | aEntry.SetText("0") 119 | } 120 | aMux.Unlock() 121 | }() 122 | sampleColor.Image = imageRgba(rgba , 36, 36) 123 | sampleColor.Refresh() 124 | updated() 125 | } 126 | 127 | rgbaEntries := fyne.NewContainerWithLayout(layout.NewGridLayout(4), rEntry, gEntry, bEntry, aEntry) 128 | 129 | slider := widget.NewSlider(0, float64(len(colorPallet)-1)) 130 | slider.Step = 1.0 131 | slider.OnChanged = func(f float64) { 132 | r(colorPallet[int(f)]) 133 | sampleColor.Image = imageRgba(rgba , 36, 36) 134 | sampleColor.Refresh() 135 | updated() 136 | } 137 | colorSelect := widget.NewSelect(pallets, func(s string){ 138 | switch s { 139 | case "Default": 140 | slider.Hide() 141 | rgba = resetRgba() 142 | rgbaEntries.Hide() 143 | spacer.Show() 144 | case "Values": 145 | slider.Hide() 146 | spacer.Hide() 147 | rEntry.SetText(fmt.Sprint(rgba.R)) 148 | gEntry.SetText(fmt.Sprint(rgba.G)) 149 | bEntry.SetText(fmt.Sprint(rgba.B)) 150 | aEntry.SetText(fmt.Sprint(rgba.A)) 151 | rgbaEntries.Show() 152 | default: 153 | slider.Show() 154 | colorPallet = palletMap[s] 155 | slider.Max = float64(len(palletMap[s])-1) 156 | slider.Value = float64(len(palletMap[s])/2) 157 | r(colorPallet[int(slider.Value)]) 158 | spacer.Hide() 159 | rgbaEntries.Hide() 160 | } 161 | sampleColor.Image = imageRgba(rgba , 36, 36) 162 | sampleColor.Refresh() 163 | slider.Refresh() 164 | updated() 165 | }) 166 | colorSelect.SetSelected("Values") 167 | 168 | sampleColor.Refresh() 169 | return fyne.NewContainerWithLayout( 170 | layout.NewGridLayout(4), 171 | widget.NewLabel(label), 172 | widget.NewHBox( 173 | widget.NewLabel("Colors"), 174 | colorSelect, 175 | ), 176 | spacer, 177 | rgbaEntries, 178 | slider, 179 | sampleColor, 180 | ) 181 | } 182 | 183 | func imageRgba(color *color.RGBA, h int, w int) *image.RGBA { 184 | img := image.NewRGBA(image.Rect(0, 0, h, w)) 185 | for y := 0; y < h; y++ { 186 | for x := 0; x < w; x++ { 187 | img.Set(x, y, color) 188 | } 189 | } 190 | return img 191 | } 192 | 193 | func checkRgbaInput(s string) uint8 { 194 | i, e := strconv.Atoi(s) 195 | if e != nil || i < 0 || i > 255 { 196 | return 0 197 | } 198 | return uint8(i) 199 | } 200 | 201 | -------------------------------------------------------------------------------- /screens/easier_src.go: -------------------------------------------------------------------------------- 1 | package screens 2 | 3 | import ( 4 | "fmt" 5 | "github.com/blockpane/prettyfyne" 6 | ) 7 | 8 | func ToEasierGoSource(config prettyfyne.PrettyThemeConfig) string { 9 | return fmt.Sprintf(`package main 10 | 11 | import ( 12 | "fyne.io/fyne" 13 | "fyne.io/fyne/app" 14 | "fyne.io/fyne/theme" 15 | "fyne.io/fyne/widget" 16 | "github.com/blockpane/prettyfyne" 17 | "image/color" 18 | "time" 19 | ) 20 | 21 | func main() { 22 | a := app.New() 23 | w := a.NewWindow("Pretty Fyne - PrettyTheme Example") 24 | 25 | myTheme := prettyfyne.PrettyTheme{ 26 | BackgroundColor: %#v, 27 | ButtonColor: %#v, 28 | DisabledButtonColor: %#v, 29 | HyperlinkColor: %#v, 30 | TextColor: %#v, 31 | DisabledTextColor: %#v, 32 | IconColor: %#v, 33 | DisabledIconColor: %#v, 34 | PlaceHolderColor: %#v, 35 | PrimaryColor: %#v, 36 | HoverColor: %#v, 37 | FocusColor: %#v, 38 | ScrollBarColor: %#v, 39 | ShadowColor: %#v, 40 | TextSize: %d, 41 | TextFont: theme.DarkTheme().TextFont(), 42 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 43 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 44 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 45 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 46 | Padding: %d, 47 | IconInlineSize: %d, 48 | ScrollBarSize: %d, 49 | ScrollBarSmallSize: %d, 50 | } 51 | 52 | hello := widget.NewLabel("Hello Pretty Fyne!") 53 | w.SetContent(widget.NewVBox( 54 | hello, 55 | widget.NewButton("Quit", func() { 56 | a.Quit() 57 | }), 58 | )) 59 | 60 | // applying a theme before the window is running will cause a panic, aggressively wait for it to be present 61 | go func(){ 62 | for { 63 | time.Sleep(50 * time.Millisecond) 64 | if hello.Visible() { 65 | break 66 | } 67 | } 68 | fyne.CurrentApp().Settings().SetTheme(myTheme.ToFyneTheme()) 69 | }() 70 | 71 | w.CenterOnScreen() 72 | w.ShowAndRun() 73 | } 74 | `, 75 | config.BackgroundColor, 76 | config.ButtonColor, 77 | config.DisabledButtonColor, 78 | config.HyperlinkColor, 79 | config.TextColor, 80 | config.DisabledTextColor, 81 | config.IconColor, 82 | config.DisabledIconColor, 83 | config.PlaceHolderColor, 84 | config.PrimaryColor, 85 | config.HoverColor, 86 | config.FocusColor, 87 | config.ScrollBarColor, 88 | config.ShadowColor, 89 | config.TextSize, 90 | config.Padding, 91 | config.IconInlineSize, 92 | config.ScrollBarSize, 93 | config.ScrollBarSmallSize, 94 | ) 95 | } 96 | -------------------------------------------------------------------------------- /screens/fonts.go: -------------------------------------------------------------------------------- 1 | package screens 2 | 3 | -------------------------------------------------------------------------------- /screens/go_src.go: -------------------------------------------------------------------------------- 1 | package screens 2 | 3 | import ( 4 | "fmt" 5 | "github.com/blockpane/prettyfyne" 6 | ) 7 | 8 | func ToGoSource(config prettyfyne.PrettyThemeConfig) string { 9 | return fmt.Sprintf(`package customtheme 10 | 11 | import ( 12 | "image/color" 13 | 14 | "fyne.io/fyne" 15 | "fyne.io/fyne/theme" 16 | ) 17 | 18 | // customTheme is based upon the custom theme example in the fyne_demo application. It is generated from the 19 | // current template in the prettyfyne theme editor and should be useable with a fyne applicaton without any 20 | // additional requirements. 21 | // It can be applied by calling: "fyne.CurrentApp().Settings().SetTheme(newCustomTheme())" after the app is running. 22 | type customTheme struct { 23 | } 24 | 25 | func (customTheme) BackgroundColor() color.Color { 26 | return %#v 27 | } 28 | 29 | func (customTheme) ButtonColor() color.Color { 30 | return %#v 31 | } 32 | 33 | func (customTheme) DisabledButtonColor() color.Color { 34 | return %#v 35 | } 36 | 37 | func (customTheme) HyperlinkColor() color.Color { 38 | return %#v 39 | } 40 | 41 | func (customTheme) TextColor() color.Color { 42 | return %#v 43 | } 44 | 45 | func (customTheme) DisabledTextColor() color.Color { 46 | return %#v 47 | } 48 | 49 | func (customTheme) IconColor() color.Color { 50 | return %#v 51 | } 52 | 53 | func (customTheme) DisabledIconColor() color.Color { 54 | return %#v 55 | } 56 | 57 | func (customTheme) PlaceHolderColor() color.Color { 58 | return %#v 59 | } 60 | 61 | func (customTheme) PrimaryColor() color.Color { 62 | return %#v 63 | } 64 | 65 | func (customTheme) HoverColor() color.Color { 66 | return %#v 67 | } 68 | 69 | func (customTheme) FocusColor() color.Color { 70 | return %#v 71 | } 72 | 73 | func (customTheme) ScrollBarColor() color.Color { 74 | return %#v 75 | } 76 | 77 | func (customTheme) ShadowColor() color.Color { 78 | return %#v 79 | } 80 | 81 | func (customTheme) TextSize() int { 82 | return %d 83 | } 84 | 85 | // TODO: for now, demo still returns default fonts 86 | func (customTheme) TextFont() fyne.Resource { 87 | return theme.DefaultTextFont() 88 | } 89 | 90 | func (customTheme) TextBoldFont() fyne.Resource { 91 | return theme.DefaultTextBoldFont() 92 | } 93 | 94 | func (customTheme) TextItalicFont() fyne.Resource { 95 | return theme.DefaultTextItalicFont() 96 | } 97 | 98 | func (customTheme) TextBoldItalicFont() fyne.Resource { 99 | return theme.DefaultTextBoldItalicFont() 100 | } 101 | 102 | func (customTheme) TextMonospaceFont() fyne.Resource { 103 | return theme.DefaultTextMonospaceFont() 104 | } 105 | 106 | func (customTheme) Padding() int { 107 | return %d 108 | } 109 | 110 | func (customTheme) IconInlineSize() int { 111 | return %d 112 | } 113 | 114 | func (customTheme) ScrollBarSize() int { 115 | return %d 116 | } 117 | 118 | func (customTheme) ScrollBarSmallSize() int { 119 | return %d 120 | } 121 | 122 | func newCustomTheme() fyne.Theme { 123 | return &customTheme{} 124 | } 125 | `, 126 | config.BackgroundColor, 127 | config.ButtonColor, 128 | config.DisabledButtonColor, 129 | config.HyperlinkColor, 130 | config.TextColor, 131 | config.DisabledTextColor, 132 | config.IconColor, 133 | config.DisabledIconColor, 134 | config.PlaceHolderColor, 135 | config.PrimaryColor, 136 | config.HoverColor, 137 | config.FocusColor, 138 | config.ScrollBarColor, 139 | config.ShadowColor, 140 | config.TextSize, 141 | config.Padding, 142 | config.IconInlineSize, 143 | config.ScrollBarSize, 144 | config.ScrollBarSmallSize, 145 | ) 146 | } 147 | -------------------------------------------------------------------------------- /screens/go_src_test.go: -------------------------------------------------------------------------------- 1 | package screens 2 | 3 | import ( 4 | "fmt" 5 | "github.com/blockpane/prettyfyne" 6 | "testing" 7 | ) 8 | 9 | func TestToGoSource(t *testing.T) { 10 | fmt.Println(ToGoSource(prettyfyne.DefaultTheme().ToConfig())) 11 | } 12 | -------------------------------------------------------------------------------- /screens/init.go: -------------------------------------------------------------------------------- 1 | package screens 2 | -------------------------------------------------------------------------------- /screens/preview.go: -------------------------------------------------------------------------------- 1 | package screens 2 | 3 | import ( 4 | "fyne.io/fyne" 5 | "fyne.io/fyne/layout" 6 | "fyne.io/fyne/theme" 7 | "fyne.io/fyne/widget" 8 | "golang.org/x/text/language" 9 | "golang.org/x/text/message" 10 | "math/rand" 11 | "net/url" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func Preview() *fyne.Container { 17 | 18 | toolbar := widget.NewToolbar(widget.NewToolbarAction(theme.MailComposeIcon(), func(){}), 19 | widget.NewToolbarSeparator(), 20 | widget.NewToolbarSpacer(), 21 | widget.NewToolbarAction(theme.ContentCutIcon(), func(){}), 22 | widget.NewToolbarAction(theme.ContentCopyIcon(), func(){}), 23 | widget.NewToolbarAction(theme.ContentPasteIcon(),func(){}), 24 | ) 25 | 26 | 27 | boldLabel := widget.NewLabelWithStyle("Bold Label", fyne.TextAlignCenter, fyne.TextStyle{ 28 | Bold: true, 29 | Italic: false, 30 | Monospace: false, 31 | }) 32 | italicLabel := widget.NewLabelWithStyle("Italic Label", fyne.TextAlignCenter, fyne.TextStyle{ 33 | Bold: false, 34 | Italic: true, 35 | Monospace: false, 36 | }) 37 | boldItalicLabel := widget.NewLabelWithStyle("Bold Italic Label", fyne.TextAlignCenter, fyne.TextStyle{ 38 | Bold: true, 39 | Italic: true, 40 | Monospace: false, 41 | }) 42 | monoLabel := widget.NewLabelWithStyle("Monospace Bold Label", fyne.TextAlignCenter, fyne.TextStyle{ 43 | Bold: true, 44 | Italic: false, 45 | Monospace: true, 46 | }) 47 | 48 | entryEnabled := widget.NewEntry() 49 | entryEnabled.SetPlaceHolder("Type Something Here.") 50 | entryDisabled := widget.NewEntry() 51 | entryDisabled.SetText("This entry is disabled") 52 | entryDisabled.Disable() 53 | entryHasText := widget.NewEntry() 54 | entryHasText.SetText("This entry already has text") 55 | 56 | buttonDisabled := widget.NewButtonWithIcon("Disabled", theme.VisibilityIcon(), func() {}) 57 | buttonDisabled.Disable() 58 | 59 | slider := widget.NewSlider(0, 10) 60 | slider.Value = 3.0 61 | 62 | pass := widget.NewPasswordEntry() 63 | pass.SetText("P@55w0rd") 64 | 65 | progLabel := widget.NewLabel("waiting . . .") 66 | progress := widget.NewProgressBar() 67 | go func() { 68 | t := time.NewTicker(50 * time.Millisecond) 69 | i := float64(0) 70 | for { 71 | select { 72 | case <-t.C: 73 | i = i + 1 74 | progress.SetValue(i / 1000) 75 | if i >= 1000 { 76 | i = 1 77 | progLabel.SetText("Starting over ... please wait.") 78 | progLabel.TextStyle = fyne.TextStyle{ 79 | Bold: true, 80 | Italic: true, 81 | } 82 | } 83 | switch { 84 | case i == 999: 85 | progLabel.SetText("Something happened. Starting over.") 86 | time.Sleep(3*time.Second) 87 | case i > 900: 88 | progLabel.SetText("Get Ready! Here it comes") 89 | progLabel.TextStyle = fyne.TextStyle{ 90 | Bold: true, 91 | Italic: false, 92 | } 93 | case i > 500: 94 | progLabel.SetText("getting closer!") 95 | case i > 100: 96 | progLabel.SetText("la dee da, la dee da dee da ....") 97 | progLabel.TextStyle = fyne.TextStyle{ 98 | Bold: false, 99 | Italic: false, 100 | } 101 | } 102 | } 103 | } 104 | }() 105 | 106 | timeRem := widget.NewLabelWithStyle("Time Remaining: 1 minute", fyne.TextAlignLeading, fyne.TextStyle{Monospace: true}) 107 | go func(){ 108 | tr := uint64(1) 109 | p := message.NewPrinter(language.AmericanEnglish) 110 | for { 111 | time.Sleep(time.Duration(rand.Intn(2)+1)*time.Second) 112 | timeRem.SetText(p.Sprintf("Time Remaining: %19d minutes", tr)) 113 | tr = tr + uint64(rand.Intn(5000)) 114 | } 115 | }() 116 | 117 | multiLineRo := widget.NewMultiLineEntry() 118 | multiLineRo.Disable() 119 | go func(){ 120 | var scrolling = make([]string, 10) 121 | rPush := func(s string) { 122 | scrolling = append(scrolling[1:], s) 123 | } 124 | for { 125 | for _, line := range scrollText { 126 | rPush(line) 127 | multiLineRo.SetText(strings.Join(scrolling, "\n")) 128 | time.Sleep(750*time.Millisecond) 129 | } 130 | } 131 | }() 132 | 133 | form := widget.NewForm() 134 | selection := widget.NewSelect([]string{"Yes", "Are you kidding?", "Sure, I'm on the clock", "No time for love Dr. Jones."}, func(s string){}) 135 | selection.SetSelected("Are you kidding?") 136 | formEntrySelects := widget.NewFormItem("Should we wait?", widget.NewHBox( 137 | selection, 138 | widget.NewSelect([]string{"one"}, func(s string){}), 139 | layout.NewSpacer(), 140 | )) 141 | form.AppendItem(formEntrySelects) 142 | 143 | radio := widget.NewRadio([]string{"Yes", "Not yet", "No thanks, it's 3am!", "I would rather have something else."}, func(s string){}) 144 | form.Append("Coffee?", widget.NewHBox(radio, layout.NewSpacer())) 145 | 146 | alreadyChecked := widget.NewCheck("Already Checked this one.", func(b bool){}) 147 | alreadyChecked.SetChecked(true) 148 | scrollGroup := widget.NewGroupWithScroller("Check these out", 149 | widget.NewCheck("Having fun?", func(b bool){}), 150 | widget.NewCheck("How about now?", func(b bool){}), 151 | alreadyChecked, 152 | widget.NewCheck("Here is another check", func(b bool){}), 153 | widget.NewCheck("Do these go on forever?", func(b bool){}), 154 | widget.NewCheck("Last one.", func(b bool){}), 155 | ) 156 | 157 | uri, _ := url.Parse("https://github.com/blockpane/prettyfyne") 158 | 159 | return fyne.NewContainerWithLayout(layout.NewMaxLayout(), 160 | widget.NewScrollContainer( 161 | widget.NewVBox( 162 | toolbar, 163 | widget.NewHBox(widget.NewHyperlink("Link to prettyfyne", uri), layout.NewSpacer(), boldLabel, layout.NewSpacer(), boldItalicLabel, layout.NewSpacer(), widget.NewLabel("This is a regular label."), layout.NewSpacer(), italicLabel), 164 | layout.NewSpacer(), 165 | fyne.NewContainerWithLayout(layout.NewGridLayout(3), slider, widget.NewIcon(theme.SettingsIcon()), 166 | fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(320, 150)), scrollGroup), 167 | ), 168 | layout.NewSpacer(), 169 | widget.NewHBox(layout.NewSpacer(), entryEnabled, pass, entryDisabled, entryHasText,layout.NewSpacer()), 170 | layout.NewSpacer(), 171 | fyne.NewContainerWithLayout(layout.NewGridLayout(3), layout.NewSpacer(), progress, progLabel), 172 | layout.NewSpacer(), 173 | widget.NewHBox(layout.NewSpacer(), widget.NewButtonWithIcon("Enabled", theme.InfoIcon(), func() {}), monoLabel, buttonDisabled, layout.NewSpacer()), 174 | layout.NewSpacer(), 175 | widget.NewHBox(layout.NewSpacer(), timeRem, layout.NewSpacer()), 176 | widget.NewProgressBarInfinite(), 177 | form, 178 | layout.NewSpacer(), 179 | widget.NewHBox(layout.NewSpacer(), 180 | fyne.NewContainerWithLayout(layout.NewFixedGridLayout(fyne.NewSize(600, 300)), 181 | multiLineRo, 182 | ), 183 | layout.NewSpacer(), 184 | ), 185 | ), 186 | ), 187 | ) 188 | } 189 | 190 | var scrollText = [...]string{ 191 | "Getting Started", 192 | "", 193 | "Using the Fyne toolkit to build cross platform applications is very simple ", 194 | "but does require some tools to be installed before you can begin.", 195 | "If your computer is set up for development with Go then the following steps may not be required, ", 196 | "but we advise reading the tips for your operating system just in case.", 197 | "If later steps in this tutorial fail then you should re-visit the prerequisites below.", 198 | "Prerequisites", 199 | "", 200 | "Fyne requires 3 basic elements to be present, the Go tools (at least version 1.12), ", 201 | "a C compiler (to connect with system graphics drivers) and an system graphics driver.", 202 | "The instructions vary depending on your operating system, ", 203 | "choose the appropriate tab below for installation instructions.", 204 | "", 205 | "Note that these steps are just required for development - ", 206 | "your Fyne applications will not require any setup or dependency installation for end users!", 207 | "", 208 | "Downloading", 209 | "After installing any prerequisites the following command will do everything to get Fyne installed:", 210 | "", 211 | "$ go get fyne.io/fyne", 212 | "", 213 | "Once that command completes you will have the full Fyne development package installed in your GOPATH.", 214 | "", 215 | "Run demo", 216 | "", 217 | "If you want to see the Fyne toolkit in action before you start to code your own application ", 218 | "then you can see our demo app running on your computer by executing:", 219 | "", 220 | "$ go get fyne.io/fyne/cmd/fyne_demo", 221 | "$ fyne_demo", 222 | "", 223 | "And that’s all there is to it! Now you can write your own Fyne application in your IDE of choice.", 224 | "", 225 | } 226 | 227 | -------------------------------------------------------------------------------- /theme.go: -------------------------------------------------------------------------------- 1 | package prettyfyne 2 | 3 | import ( 4 | "fyne.io/fyne" 5 | "fyne.io/fyne/theme" 6 | "gopkg.in/yaml.v2" 7 | "image/color" 8 | ) 9 | 10 | type PrettyTheme struct { 11 | BackgroundColor color.Color 12 | ButtonColor color.Color 13 | DisabledButtonColor color.Color 14 | HyperlinkColor color.Color 15 | TextColor color.Color 16 | DisabledTextColor color.Color 17 | IconColor color.Color 18 | DisabledIconColor color.Color 19 | PlaceHolderColor color.Color 20 | PrimaryColor color.Color 21 | HoverColor color.Color 22 | FocusColor color.Color 23 | ScrollBarColor color.Color 24 | ShadowColor color.Color 25 | TextSize int 26 | TextFont fyne.Resource 27 | textFontPath string 28 | TextBoldFont fyne.Resource 29 | textBoldFontPath string 30 | TextItalicFont fyne.Resource 31 | textItalicFontPath string 32 | TextBoldItalicFont fyne.Resource 33 | textBoldItalicFontPath string 34 | TextMonospaceFont fyne.Resource 35 | textMonospaceFontPath string 36 | Padding int 37 | IconInlineSize int 38 | ScrollBarSize int 39 | ScrollBarSmallSize int 40 | } 41 | 42 | // DefaultTheme returns the default fyne dark theme, which provides an easy starting point for customization 43 | func DefaultTheme() *PrettyTheme { 44 | return &PrettyTheme{ 45 | BackgroundColor: theme.DarkTheme().BackgroundColor(), 46 | ButtonColor: theme.DarkTheme().ButtonColor(), 47 | DisabledButtonColor: theme.DarkTheme().DisabledButtonColor(), 48 | HyperlinkColor: theme.DarkTheme().HyperlinkColor(), 49 | TextColor: theme.DarkTheme().TextColor(), 50 | DisabledTextColor: theme.DarkTheme().DisabledIconColor(), 51 | IconColor: theme.DarkTheme().IconColor(), 52 | DisabledIconColor: theme.DarkTheme().DisabledIconColor(), 53 | PlaceHolderColor: theme.DarkTheme().PlaceHolderColor(), 54 | PrimaryColor: theme.DarkTheme().PrimaryColor(), 55 | HoverColor: theme.DarkTheme().HoverColor(), 56 | FocusColor: theme.DarkTheme().FocusColor(), 57 | ScrollBarColor: theme.DarkTheme().ScrollBarColor(), 58 | ShadowColor: theme.DarkTheme().ShadowColor(), 59 | TextSize: theme.DarkTheme().TextSize(), 60 | TextFont: theme.DarkTheme().TextFont(), 61 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 62 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 63 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 64 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 65 | Padding: theme.DarkTheme().Padding(), 66 | IconInlineSize: theme.DarkTheme().IconInlineSize(), 67 | ScrollBarSize: theme.DarkTheme().ScrollBarSize(), 68 | ScrollBarSmallSize: theme.DarkTheme().ScrollBarSmallSize(), 69 | } 70 | } 71 | 72 | // DefaultLightTheme returns the default fyne light theme, which provides an easy starting point for customization 73 | func DefaultLightTheme() *PrettyTheme { 74 | return &PrettyTheme{ 75 | BackgroundColor: theme.LightTheme().BackgroundColor(), 76 | ButtonColor: theme.LightTheme().ButtonColor(), 77 | DisabledButtonColor: theme.LightTheme().DisabledButtonColor(), 78 | HyperlinkColor: theme.LightTheme().HyperlinkColor(), 79 | TextColor: theme.LightTheme().TextColor(), 80 | DisabledTextColor: theme.LightTheme().DisabledIconColor(), 81 | IconColor: theme.LightTheme().IconColor(), 82 | DisabledIconColor: theme.LightTheme().DisabledIconColor(), 83 | PlaceHolderColor: theme.LightTheme().PlaceHolderColor(), 84 | PrimaryColor: theme.LightTheme().PrimaryColor(), 85 | HoverColor: theme.LightTheme().HoverColor(), 86 | FocusColor: theme.LightTheme().FocusColor(), 87 | ScrollBarColor: theme.LightTheme().ScrollBarColor(), 88 | ShadowColor: theme.LightTheme().ShadowColor(), 89 | TextSize: theme.LightTheme().TextSize(), 90 | TextFont: theme.LightTheme().TextFont(), 91 | TextBoldFont: theme.LightTheme().TextBoldFont(), 92 | TextItalicFont: theme.LightTheme().TextItalicFont(), 93 | TextBoldItalicFont: theme.LightTheme().TextBoldItalicFont(), 94 | TextMonospaceFont: theme.LightTheme().TextMonospaceFont(), 95 | Padding: theme.LightTheme().Padding(), 96 | IconInlineSize: theme.LightTheme().IconInlineSize(), 97 | ScrollBarSize: theme.LightTheme().ScrollBarSize(), 98 | ScrollBarSmallSize: theme.LightTheme().ScrollBarSmallSize(), 99 | } 100 | } 101 | 102 | 103 | // ToFyneTheme converts a PrettyTheme to the fyne.Theme interface that can be applied to the app 104 | func (pt PrettyTheme) ToFyneTheme() fyne.Theme { 105 | return customTheme{&pt} 106 | } 107 | 108 | func (pt PrettyTheme) MarshalYaml() ([]byte, error) { 109 | return yaml.Marshal(pt.ToConfig()) 110 | } 111 | 112 | func (pt PrettyTheme) ToConfig() PrettyThemeConfig { 113 | r := func(c color.Color) *color.RGBA { 114 | r, g, b, a := c.RGBA() 115 | return &color.RGBA{ 116 | R: uint8(r), 117 | G: uint8(g), 118 | B: uint8(b), 119 | A: uint8(a), 120 | } 121 | } 122 | 123 | return PrettyThemeConfig{ 124 | BackgroundColor: r(pt.BackgroundColor), 125 | ButtonColor: r(pt.ButtonColor), 126 | DisabledButtonColor: r(pt.DisabledButtonColor), 127 | HyperlinkColor: r(pt.HyperlinkColor), 128 | TextColor: r(pt.TextColor), 129 | DisabledTextColor: r(pt.DisabledTextColor), 130 | IconColor: r(pt.IconColor), 131 | DisabledIconColor: r(pt.DisabledIconColor), 132 | PlaceHolderColor: r(pt.PlaceHolderColor), 133 | PrimaryColor: r(pt.PrimaryColor), 134 | HoverColor: r(pt.HoverColor), 135 | FocusColor: r(pt.FocusColor), 136 | ScrollBarColor: r(pt.ScrollBarColor), 137 | ShadowColor: r(pt.ShadowColor), 138 | TextSize: pt.TextSize, 139 | TextFontPath: pt.textFontPath, 140 | TextFont: pt.TextFont.Name(), 141 | TextBoldFontPath: pt.textBoldFontPath, 142 | TextBoldFont: pt.TextBoldFont.Name(), 143 | TextItalicFontPath: pt.textItalicFontPath, 144 | TextItalicFont: pt.TextItalicFont.Name(), 145 | TextBoldItalicFontPath: pt.textBoldItalicFontPath, 146 | TextBoldItalicFont: pt.TextBoldItalicFont.Name(), 147 | TextMonospaceFontPath: pt.textMonospaceFontPath, 148 | TextMonospaceFont: pt.TextMonospaceFont.Name(), 149 | Padding: pt.Padding, 150 | IconInlineSize: pt.IconInlineSize, 151 | ScrollBarSize: pt.ScrollBarSize, 152 | ScrollBarSmallSize: pt.ScrollBarSmallSize, 153 | } 154 | } 155 | 156 | var ExampleMaterialLight = PrettyTheme{ 157 | BackgroundColor: color.RGBA{255,255,255,255}, 158 | ButtonColor: color.RGBA{0,0,0,10}, 159 | DisabledButtonColor: color.RGBA{0,0,0,30}, 160 | HyperlinkColor: color.RGBA{100,181,246,253}, 161 | TextColor: color.RGBA{0,0,0,222}, 162 | DisabledTextColor: color.RGBA{94,94,94,255}, 163 | IconColor: color.RGBA{100,181,246,253}, 164 | DisabledIconColor: color.RGBA{0,0,0,97}, 165 | PlaceHolderColor: color.RGBA{178,178,178,255}, 166 | PrimaryColor: color.RGBA{166,212,250,255}, 167 | HoverColor: color.RGBA{0,0,0,30}, 168 | FocusColor: color.RGBA{198,198,198,224}, 169 | ScrollBarColor: color.RGBA{0,0,0,153}, 170 | ShadowColor: color.RGBA{0,0,0,66}, 171 | TextSize: 13, 172 | TextFont: theme.LightTheme().TextFont(), 173 | TextBoldFont: theme.LightTheme().TextBoldFont(), 174 | TextItalicFont: theme.LightTheme().TextItalicFont(), 175 | TextBoldItalicFont: theme.LightTheme().TextBoldItalicFont(), 176 | TextMonospaceFont: theme.LightTheme().TextMonospaceFont(), 177 | Padding: 4, 178 | IconInlineSize: 24, 179 | ScrollBarSize: 12, 180 | ScrollBarSmallSize: 3, 181 | } 182 | 183 | var ExampleEveryDayIsHalloween = PrettyTheme{ 184 | BackgroundColor: color.RGBA{R: 30, G: 30, B: 30, A: 255}, 185 | ButtonColor: color.RGBA{R: 20, G: 20, B: 20, A: 255}, 186 | DisabledButtonColor: color.RGBA{R: 15, G: 15, B: 17, A: 255}, 187 | HyperlinkColor: color.RGBA{R:0xaa, G:0x64, B:0x14, A:0x40}, 188 | TextColor: color.RGBA{R: 200, G: 200, B: 200, A: 255}, 189 | DisabledTextColor: color.RGBA{R: 155, G: 155, B: 155, A: 127}, 190 | IconColor: color.RGBA{R: 150, G: 80, B: 0, A: 255}, 191 | DisabledIconColor: color.RGBA{R: 155, G: 155, B: 155, A: 127}, 192 | PlaceHolderColor: color.RGBA{R: 150, G: 80, B: 0, A: 255}, 193 | PrimaryColor: color.RGBA{R: 110, G: 40, B: 0, A: 127}, 194 | HoverColor: color.RGBA{R: 0, G: 0, B: 0, A: 255}, 195 | FocusColor: color.RGBA{R: 99, G: 99, B: 99, A: 255}, 196 | ScrollBarColor: color.RGBA{R: 35, G: 35, B: 35, A: 8}, 197 | ShadowColor: color.RGBA{R: 0, G: 0, B: 0, A: 64}, 198 | TextSize: 13, 199 | TextFont: theme.DarkTheme().TextFont(), 200 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 201 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 202 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 203 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 204 | Padding: 4, 205 | IconInlineSize: 24, 206 | ScrollBarSize: 10, 207 | ScrollBarSmallSize: 4, 208 | } 209 | 210 | 211 | var ExampleTree = PrettyTheme{ 212 | BackgroundColor: &color.RGBA{R:0x70, G:0x78, B:0x4c, A:0xf0}, 213 | ButtonColor: &color.RGBA{R:0x70, G:0x78, B:0x4c, A:0xd2}, 214 | DisabledButtonColor: &color.RGBA{R:0x70, G:0x78, B:0x4c, A:0xff}, 215 | HyperlinkColor: &color.RGBA{R:0x1, G:0x24, B:0x1, A:0xff}, 216 | TextColor: &color.RGBA{R:0x0, G:0x0, B:0x0, A:0xff}, 217 | DisabledTextColor: &color.RGBA{R:0x2a, G:0x32, B:0x6, A:0xff}, 218 | IconColor: &color.RGBA{R:0x3c, G:0x34, B:0xf, A:0xff}, 219 | DisabledIconColor: &color.RGBA{R:0x5c, G:0x64, B:0x38, A:0xff}, 220 | PlaceHolderColor: &color.RGBA{R:0x57, G:0x2c, B:0x1, A:0xff}, 221 | PrimaryColor: &color.RGBA{R:0x70, G:0x78, B:0x4c, A:0xb4}, 222 | HoverColor: &color.RGBA{R:0x70, G:0x78, B:0x4c, A:0xc8}, 223 | FocusColor: &color.RGBA{R:0xa0, G:0x96, B:0x32, A:0xff}, 224 | ScrollBarColor: &color.RGBA{R:0x23, G:0x23, B:0x23, A:0x8}, 225 | ShadowColor: &color.RGBA{R:0x0, G:0x0, B:0x0, A:0x40}, 226 | TextSize: 15, 227 | TextFont: theme.DarkTheme().TextFont(), 228 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 229 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 230 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 231 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 232 | Padding: 4, 233 | IconInlineSize: 28, 234 | ScrollBarSize: 10, 235 | ScrollBarSmallSize: 4, 236 | } 237 | 238 | var ExampleDracula = PrettyTheme{ 239 | BackgroundColor: &color.RGBA{R:0x0, G:0x0, B:0x0, A:0xff}, 240 | ButtonColor: &color.RGBA{R:0xf, G:0xf, B:0xf, A:0xff}, 241 | DisabledButtonColor: &color.RGBA{R:0xf, G:0xf, B:0x11, A:0xff}, 242 | HyperlinkColor: &color.RGBA{R:0x0, G:0x41, B:0x78, A:0xff}, 243 | TextColor: &color.RGBA{R:0xaf, G:0xaf, B:0xaf, A:0xff}, 244 | DisabledTextColor: &color.RGBA{R:0x4e, G:0x4e, B:0x4e, A:0xff}, 245 | IconColor: &color.RGBA{R:0x53, G:0x0, B:0x0, A:0xff}, 246 | DisabledIconColor: &color.RGBA{R:0x21, G:0x21, B:0x21, A:0xff}, 247 | PlaceHolderColor: &color.RGBA{R:0x64, G:0x14, B:0x27, A:0xff}, 248 | PrimaryColor: &color.RGBA{R:0x50, G:0x0, B:0x13, A:0xff}, 249 | HoverColor: &color.RGBA{R:0x1a, G:0x12, B:0xe, A:0xff}, 250 | FocusColor: &color.RGBA{R:0x28, G:0x0, B:0x9, A:0x80}, 251 | ScrollBarColor: &color.RGBA{R:0x23, G:0x23, B:0x23, A:0x8}, 252 | ShadowColor: &color.RGBA{R:0x12, G:0x12, B:0x12, A:0xff}, 253 | TextSize: 16, 254 | TextFont: theme.DarkTheme().TextFont(), 255 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 256 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 257 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 258 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 259 | Padding: 3, 260 | IconInlineSize: 23, 261 | ScrollBarSize: 10, 262 | ScrollBarSmallSize: 4, 263 | } 264 | 265 | var ExampleCubicleLife = PrettyTheme{ 266 | BackgroundColor: &color.RGBA{R:0xd3, G:0xd3, B:0xd3, A:0xff}, 267 | ButtonColor: &color.RGBA{R:0xbf, G:0xbf, B:0xbf, A:0xff}, 268 | DisabledButtonColor: &color.RGBA{R:0x87, G:0x87, B:0x87, A:0xff}, 269 | HyperlinkColor: &color.RGBA{R:0x0, G:0x33, B:0xff, A:0xff}, 270 | TextColor: &color.RGBA{R:0x0, G:0x0, B:0x0, A:0xff}, 271 | DisabledTextColor: &color.RGBA{R:0x1a, G:0x1a, B:0x1a, A:0xff}, 272 | IconColor: &color.RGBA{R:0x25, G:0x25, B:0x25, A:0xff}, 273 | DisabledIconColor: &color.RGBA{R:0xa9, G:0xa9, B:0xa9, A:0xff}, 274 | PlaceHolderColor: &color.RGBA{R:0x94, G:0x94, B:0x94, A:0xff}, 275 | PrimaryColor: &color.RGBA{R:0x33, G:0x66, B:0xcc, A:0xf0}, 276 | HoverColor: &color.RGBA{R:0x8f, G:0x8f, B:0x8f, A:0xff}, 277 | FocusColor: &color.RGBA{R:0xa7, G:0xbd, B:0xdf, A:0xff}, 278 | ScrollBarColor: &color.RGBA{R:0x4d, G:0x4d, B:0x4d, A:0xff}, 279 | ShadowColor: &color.RGBA{R:0x0, G:0x0, B:0x0, A:0x40}, 280 | TextSize: 15, 281 | TextFont: theme.DarkTheme().TextFont(), 282 | TextBoldFont: theme.DarkTheme().TextBoldFont(), 283 | TextItalicFont: theme.DarkTheme().TextItalicFont(), 284 | TextBoldItalicFont: theme.DarkTheme().TextBoldItalicFont(), 285 | TextMonospaceFont: theme.DarkTheme().TextMonospaceFont(), 286 | Padding: 6, 287 | IconInlineSize: 32, 288 | ScrollBarSize: 10, 289 | ScrollBarSmallSize: 4, 290 | } 291 | -------------------------------------------------------------------------------- /theme_test.go: -------------------------------------------------------------------------------- 1 | package prettyfyne 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestPrettyTheme_MarshalYaml(t *testing.T) { 9 | pt := DefaultTheme() 10 | y, err := pt.MarshalYaml() 11 | if err != nil { 12 | t.Error(err) 13 | } else { 14 | fmt.Println(string(y)) 15 | } 16 | } 17 | 18 | func TestUnmarshalYaml(t *testing.T) { 19 | yt := `background_color: 20 | r: 66 21 | g: 66 22 | b: 66 23 | a: 255 24 | button_color: 25 | r: 33 26 | g: 33 27 | b: 33 28 | a: 255 29 | disabled_button_color: 30 | r: 49 31 | g: 49 32 | b: 49 33 | a: 255 34 | hyperlink_color: 35 | r: 153 36 | g: 153 37 | b: 255 38 | a: 255 39 | text_color: 40 | r: 255 41 | g: 255 42 | b: 255 43 | a: 255 44 | disabled_text_color: 45 | r: 96 46 | g: 96 47 | b: 96 48 | a: 255 49 | icon_color: 50 | r: 255 51 | g: 255 52 | b: 255 53 | a: 255 54 | disabled_icon_color: 55 | r: 96 56 | g: 96 57 | b: 96 58 | a: 255 59 | place_holder_color: 60 | r: 178 61 | g: 178 62 | b: 178 63 | a: 255 64 | primary_color: 65 | r: 26 66 | g: 35 67 | b: 126 68 | a: 255 69 | hover_color: 70 | r: 49 71 | g: 49 72 | b: 49 73 | a: 255 74 | focus_color: 75 | r: 26 76 | g: 35 77 | b: 126 78 | a: 255 79 | scroll_bar_color: 80 | r: 0 81 | g: 0 82 | b: 0 83 | a: 153 84 | shadow_color: 85 | r: 0 86 | g: 0 87 | b: 0 88 | a: 102 89 | text_size: 14 90 | text_font: NotoSans-Regular.ttf 91 | text_bold_font: NotoSans-Bold.ttf 92 | text_italic_font: NotoSans-Italic.ttf 93 | text_bold_italic_font: NotoSans-BoldItalic.ttf 94 | text_monospace_font: NotoMono-Regular.ttf 95 | padding: 4 96 | icon_inline_size: 20 97 | scroll_bar_size: 16 98 | scroll_bar_small_size: 3 99 | ` 100 | _, ok, err := UnmarshalYaml([]byte(yt)) 101 | if err != nil { 102 | t.Error(err) 103 | } 104 | if !ok { 105 | t.Error("didn't load fonts!") 106 | } 107 | } 108 | 109 | --------------------------------------------------------------------------------