1// Copyright 2019 Google Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package donut 16 17// options.go contains configurable options for Donut. 18 19import ( 20 "fmt" 21 22 "github.com/mum4k/termdash/align" 23 "github.com/mum4k/termdash/cell" 24) 25 26// Option is used to provide options. 27type Option interface { 28 // set sets the provided option. 29 set(*options) 30} 31 32// option implements Option. 33type option func(*options) 34 35// set implements Option.set. 36func (o option) set(opts *options) { 37 o(opts) 38} 39 40// options holds the provided options. 41type options struct { 42 donutHolePercent int 43 hideTextProgress bool 44 45 textCellOpts []cell.Option 46 cellOpts []cell.Option 47 48 labelCellOpts []cell.Option 49 labelAlign align.Horizontal 50 label string 51 52 // The angle in degrees that represents 0 and 100% of the progress. 53 startAngle int 54 // The direction in which the donut completes as progress increases. 55 // Positive for counter-clockwise, negative for clockwise. 56 direction int 57} 58 59// validate validates the provided options. 60func (o *options) validate() error { 61 if min, max := 0, 100; o.donutHolePercent < min || o.donutHolePercent > max { 62 return fmt.Errorf("invalid donut hole percent %d, must be in range %d <= p <= %d", o.donutHolePercent, min, max) 63 } 64 65 if min, max := 0, 360; o.startAngle < min || o.startAngle >= max { 66 return fmt.Errorf("invalid start angle %d, must be in range %d <= angle < %d", o.startAngle, min, max) 67 } 68 69 return nil 70} 71 72// newOptions returns options with the default values set. 73func newOptions() *options { 74 return &options{ 75 donutHolePercent: DefaultHolePercent, 76 startAngle: DefaultStartAngle, 77 direction: -1, 78 textCellOpts: []cell.Option{ 79 cell.FgColor(cell.ColorDefault), 80 cell.BgColor(cell.ColorDefault), 81 }, 82 labelAlign: DefaultLabelAlign, 83 } 84} 85 86// DefaultHolePercent is the default value for the HolePercent 87// option. 88const DefaultHolePercent = 35 89 90// HolePercent sets the size of the "hole" inside the donut as a 91// percentage of the donut's radius. 92// Setting this to zero disables the hole so that the donut will become just a 93// circle. Valid range is 0 <= p <= 100. 94func HolePercent(p int) Option { 95 return option(func(opts *options) { 96 opts.donutHolePercent = p 97 }) 98} 99 100// ShowTextProgress configures the Gauge so that it also displays a text 101// enumerating the progress. This is the default behavior. 102// If the progress is set by a call to Percent(), the displayed text will show 103// the percentage, e.g. "50%". If the progress is set by a call to Absolute(), 104// the displayed text will those the absolute numbers, e.g. "5/10". 105// 106// The progress is only displayed if there is enough space for it in the middle 107// of the drawn donut. 108// 109// Providing this option also sets HolePercent to its default value. 110func ShowTextProgress() Option { 111 return option(func(opts *options) { 112 opts.hideTextProgress = false 113 }) 114} 115 116// HideTextProgress disables the display of a text enumerating the progress. 117func HideTextProgress() Option { 118 return option(func(opts *options) { 119 opts.hideTextProgress = true 120 }) 121} 122 123// TextCellOpts sets cell options on cells that contain the displayed text 124// progress. 125func TextCellOpts(cOpts ...cell.Option) Option { 126 return option(func(opts *options) { 127 opts.textCellOpts = cOpts 128 }) 129} 130 131// CellOpts sets cell options on cells that contain the donut. 132func CellOpts(cOpts ...cell.Option) Option { 133 return option(func(opts *options) { 134 opts.cellOpts = cOpts 135 }) 136} 137 138// DefaultStartAngle is the default value for the StartAngle option. 139const DefaultStartAngle = 90 140 141// StartAngle sets the starting angle in degrees, i.e. the point that will 142// represent both 0% and 100% of progress. 143// Valid values are in range 0 <= angle < 360. 144// Angles start at the X axis and grow counter-clockwise. 145func StartAngle(angle int) Option { 146 return option(func(opts *options) { 147 opts.startAngle = angle 148 }) 149} 150 151// Clockwise sets the donut widget for a progression in the clockwise 152// direction. This is the default option. 153func Clockwise() Option { 154 return option(func(opts *options) { 155 opts.direction = -1 156 }) 157} 158 159// CounterClockwise sets the donut widget for a progression in the counter-clockwise 160// direction. 161func CounterClockwise() Option { 162 return option(func(opts *options) { 163 opts.direction = 1 164 }) 165} 166 167// Label sets a text label to be displayed under the donut. 168func Label(text string, cOpts ...cell.Option) Option { 169 return option(func(opts *options) { 170 opts.label = text 171 opts.labelCellOpts = cOpts 172 }) 173} 174 175// DefaultLabelAlign is the default value for the LabelAlign option. 176const DefaultLabelAlign = align.HorizontalCenter 177 178// LabelAlign sets the alignment of the label under the donut. 179func LabelAlign(la align.Horizontal) Option { 180 return option(func(opts *options) { 181 opts.labelAlign = la 182 }) 183} 184