├── LICENSE ├── README.md ├── component.go ├── components ├── progress.go ├── sparkline.go ├── spinner.go └── stopwatch.go ├── context.go ├── document.go ├── document_test.go ├── examples ├── counter.go ├── layout.go └── sparkline.go ├── finalize.go ├── flex ├── LICENSE ├── PATENTS ├── README.md ├── absolute_position_test.go ├── align_content_test.go ├── align_items_test.go ├── align_self_test.go ├── aspect_ratio_test.go ├── baseline_func_test.go ├── border_test.go ├── compute_margin_test.go ├── compute_padding_test.go ├── default_values_test.go ├── dimension_test.go ├── dirty_marking_test.go ├── display_test.go ├── edge_test.go ├── enums.go ├── flex_direction_test.go ├── flex_test.go ├── flex_wrap_test.go ├── had_overflow_test.go ├── issue5_test.go ├── justify_content_test.go ├── margin_test.go ├── math.go ├── math_test.go ├── measure_cache_test.go ├── measure_mode_test.go ├── measure_test.go ├── min_max_dimension_test.go ├── node_child_test.go ├── padding_test.go ├── percentage_test.go ├── print.go ├── relayout_test.go ├── rounding_function_test.go ├── rounding_measure_func_test.go ├── rounding_test.go ├── size_overflow_test.go ├── style_test.go ├── yoga.go ├── yoga_h.go └── yoga_props.go ├── fragment.go ├── go.mod ├── go.sum ├── internal └── layout │ └── builder.go ├── layout.go ├── layout_test.go ├── measure.go ├── measure_test.go ├── renderer.go ├── renderer_string.go ├── renderer_string_test.go ├── renderer_term.go ├── style.go ├── testing.go ├── text.go └── tree.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mitchell Hashimoto 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 | # go-glint [](https://godoc.org/github.com/mitchellh/go-glint) 2 | 3 | Glint is a component-based UI framework specifically targeted towards 4 | command-line interfaces. This allows you to create highly dynamic CLI interfaces 5 | using shared, easily testable components. Glint uses a Flexbox implementation 6 | to make it easy to lay out components in the CLI, including paddings, margins, 7 | and more. 8 | 9 | **API Status: Unstable.** We're still actively working on the API and 10 | may change it in backwards incompatible ways. See the roadmap section in 11 | particular for work that may impact the API. In particular, we have 12 | integrated this library into [Waypoint](https://github.com/hashicorp/waypoint), 13 | and the experience of using this library in the real world will likely drive major 14 | changes. 15 | 16 | ## Example 17 | 18 | The example below shows a simple dynamic counter: 19 | 20 | ```go 21 | func main() { 22 | var counter uint32 23 | go func() { 24 | for { 25 | time.Sleep(100 * time.Millisecond) 26 | atomic.AddUint32(&counter, 1) 27 | } 28 | }() 29 | 30 | d := glint.New() 31 | d.Append( 32 | glint.Style( 33 | glint.TextFunc(func(rows, cols uint) string { 34 | return fmt.Sprintf("%d tests passed", atomic.LoadUint32(&counter)) 35 | }), 36 | glint.Color("green"), 37 | ), 38 | ) 39 | d.Render(context.Background()) 40 | } 41 | ``` 42 | 43 | Output: 44 | 45 |  46 | 47 | ## Roadmap 48 | 49 | Glint is still an early stage project and there is a lot that we want to 50 | improve on. This may introduce some backwards incompatibilities but we are 51 | trying to stabilize the API as quickly as possible. 52 | 53 | * **Non-interactive interfaces.** We want to add support for rendering to 54 | non-interactive interfaces and allowing components to provide custom behavior 55 | in these cases. For now, users of Glint should detect non-interactivity and 56 | avoid using Glint. 57 | 58 | * **Windows PowerShell and Cmd.** Glint works fine in ANSI-compatible terminals 59 | on Windows, but doesn't work with PowerShell and Cmd. We want to make this 60 | work. 61 | 62 | * **Dirty tracking.** Glint currently rerenders the entire frame on each 63 | tick. I'd like components to be able to report if there are changes (if they 64 | are "dirty") and need to be rerendered. We could then more efficiently 65 | recalculate layouts and rerender outputs. 66 | 67 | * **User Input.** Glint should be able to query for user input and render 68 | this within its existing set of components. 69 | 70 | * **Expose styling to custom renderers.** Currently the `Style` component 71 | is a special-case for the terminal renderer to render colors. I'd like to expose 72 | the styles in a way that other renderers could use it in some meaningful way. 73 | 74 | ## Thanks 75 | 76 | This library is heavily inspired by the [Ink project](https://github.com/vadimdemedes/ink). 77 | I saw this project and thought that having a central render loop along with 78 | a full layout engine was a fantastic idea. Most of my projects are in Go 79 | so I wanted to be able to realize these benefits with Go. Thank you! 80 | -------------------------------------------------------------------------------- /component.go: -------------------------------------------------------------------------------- 1 | package glint 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/mitchellh/go-glint/internal/layout" 7 | ) 8 | 9 | // Components are the individual items that are rendered within a document. 10 | type Component interface { 11 | // Body returns the body of this component. This can be another custom 12 | // component or a standard component such as Text. 13 | // 14 | // The context parameter is used to send parameters across multiple 15 | // components. It should not be used for timeouts; Body should aim to 16 | // not block ever since this can block the render loop. 17 | // 18 | // Components are highly encouraged to support finalization (see 19 | // ComponentFinalizer). Components can finalize early by wrapping 20 | // their body in a Finalize built-in component. Finalization allows 21 | // the renderer to highly optimize output. 22 | Body(context.Context) Component 23 | } 24 | 25 | // ComponentFinalizer allows components to be notified they are going to 26 | // be finalized. A finalized component may never be re-rendered again. The 27 | // next call to Body should be considered the final call. 28 | // 29 | // In a Document, if the component list has a set of finalized components 30 | // at the front, the renderer will draw it once and only re-draw non-finalized 31 | // components. For example, consider a document that is a set of text components 32 | // followed by a progress bar. If the text components are static, then they 33 | // will be written to the output once and only the progress bar will redraw. 34 | // 35 | // Currently, Body may be called multiple times after Finalize. Implementers 36 | // should return the same result after being finalized. 37 | type ComponentFinalizer interface { 38 | Component 39 | 40 | // Finalize notifies the component that it will be finalized. This may 41 | // be called multiple times. 42 | Finalize() 43 | } 44 | 45 | // ComponentMounter allows components to be notified when they are 46 | // mounted and unmounted. A mounted component is one that is added to 47 | // a render tree for the first time. A component is unmounted when it is 48 | // removed from the render tree. 49 | // 50 | // The callbacks here may be called multiple times under certain scenarios: 51 | // (1) a component is used in multiple Document instances, (2) a component 52 | // is unmounted and then remounted in the future. 53 | // 54 | // A component mounted multiple times in the same render tree does NOT 55 | // have the mount callbacks called multiple times. 56 | // 57 | // A good use case for this interface is setting up and cleaning up resources. 58 | type ComponentMounter interface { 59 | Component 60 | 61 | // Mount is called when the component is added to a render tree. The 62 | // context given to this is used to access data set by Glint and the 63 | // renderer in use. 64 | Mount(context.Context) 65 | 66 | // Unmount is called when the component is removed from a render tree. 67 | // This will be called under ANY scenario where the component is 68 | // removed from the render tree, including finalization. 69 | Unmount(context.Context) 70 | } 71 | 72 | // componentLayout can be implemented to set custom layout settings 73 | // for the component. This can only be implemented by internal components 74 | // since we use an internal library. 75 | // 76 | // End users should use the "Layout" component to set layout options. 77 | type componentLayout interface { 78 | Component 79 | 80 | // Layout should return the layout settings for this component. 81 | Layout() *layout.Builder 82 | } 83 | 84 | // terminalComponent is an embeddable struct for internal usage that 85 | // satisfies Component. This is used since terminal components are handled 86 | // as special cases. 87 | type terminalComponent struct{} 88 | 89 | func (terminalComponent) Body(context.Context) Component { return nil } 90 | -------------------------------------------------------------------------------- /components/progress.go: -------------------------------------------------------------------------------- 1 | package components 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cheggaaa/pb/v3" 7 | "github.com/mitchellh/go-glint" 8 | ) 9 | 10 | // ProgressElement renders a progress bar. This wraps the cheggaaa/pb package 11 | // since that provides important functionality. This uses single call renders 12 | // to render the progress bar as values change. 13 | type ProgressElement struct { 14 | *pb.ProgressBar 15 | } 16 | 17 | // Progress creates a new progress bar element with the given total. 18 | // For more fine-grained control, please construct a ProgressElement 19 | // directly. 20 | func Progress(total int) *ProgressElement { 21 | return &ProgressElement{ 22 | ProgressBar: pb.New(total), 23 | } 24 | } 25 | 26 | func (el *ProgressElement) Body(context.Context) glint.Component { 27 | // If we have no progress bar render nothing. 28 | if el.ProgressBar == nil { 29 | return nil 30 | } 31 | 32 | // Write the current progress 33 | return glint.TextFunc(func(rows, cols uint) string { 34 | el.ProgressBar.SetWidth(int(cols)) 35 | 36 | return el.ProgressBar.String() 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /components/sparkline.go: -------------------------------------------------------------------------------- 1 | package components 2 | 3 | import ( 4 | "container/ring" 5 | "context" 6 | "math" 7 | "sync" 8 | 9 | "github.com/mitchellh/go-glint" 10 | ) 11 | 12 | // SparklineComponent renders a sparkline graph. 13 | type SparklineComponent struct { 14 | sync.Mutex 15 | 16 | // If set, this will style the peak value. 17 | PeakStyle []glint.StyleOption 18 | 19 | values *ring.Ring 20 | } 21 | 22 | // Sparkline creates a SparklineComponent with the given set of initial values. 23 | // These initial values will also specify the max width for this sparkline 24 | // unless values are replaced with Set. 25 | func Sparkline(values []uint) *SparklineComponent { 26 | var c SparklineComponent 27 | c.Set(values) 28 | return &c 29 | } 30 | 31 | // Set sets the full set of values to the given slice. This will also reset 32 | // the size of the sparkline to this length. 33 | func (c *SparklineComponent) Set(values []uint) { 34 | c.Lock() 35 | defer c.Unlock() 36 | c.values = ring.New(len(values)) 37 | for _, v := range values { 38 | c.values.Value = v 39 | c.values = c.values.Next() 40 | } 41 | } 42 | 43 | // Append adds the given values to the end of the values buffer. The buffer 44 | // size is determined by the values list given in Sparkline or Set. This will 45 | // overwrite the oldest values. 46 | func (c *SparklineComponent) Append(values ...uint) { 47 | c.Lock() 48 | defer c.Unlock() 49 | for _, v := range values { 50 | c.values.Value = v 51 | c.values = c.values.Next() 52 | } 53 | } 54 | 55 | func (c *SparklineComponent) valuesSlice() []uint { 56 | result := make([]uint, c.values.Len()) 57 | for i := range result { 58 | result[i] = c.values.Value.(uint) 59 | c.values = c.values.Next() 60 | } 61 | 62 | return result 63 | } 64 | 65 | func (c *SparklineComponent) Body(context.Context) glint.Component { 66 | c.Lock() 67 | defer c.Unlock() 68 | 69 | values := c.valuesSlice() 70 | 71 | // If we have nothing we render nothing 72 | if len(values) == 0 { 73 | return nil 74 | } 75 | 76 | // Find the max 77 | max := values[0] 78 | if len(values) > 1 { 79 | for _, v := range values[1:] { 80 | if v > max { 81 | max = v 82 | } 83 | } 84 | } 85 | 86 | // Build each symbol 87 | peak := false 88 | parts := make([]glint.Component, len(values)) 89 | for i, v := range values { 90 | symbolIdx := int(math.Ceil(float64(v) / float64(max) * float64(len(sparklineSymbols)-1))) 91 | parts[i] = glint.Text(string(sparklineSymbols[symbolIdx])) 92 | 93 | if len(c.PeakStyle) > 0 && v == max && !peak { 94 | peak = true 95 | parts[i] = glint.Style(parts[i], c.PeakStyle...) 96 | } 97 | } 98 | 99 | // Render them in a row 100 | return glint.Layout(parts...).Row() 101 | } 102 | 103 | var sparklineSymbols = []rune{ 104 | '\u2581', 105 | '\u2582', 106 | '\u2583', 107 | '\u2584', 108 | '\u2585', 109 | '\u2586', 110 | '\u2587', 111 | '\u2588', 112 | } 113 | -------------------------------------------------------------------------------- /components/spinner.go: -------------------------------------------------------------------------------- 1 | package components 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/mitchellh/go-glint" 8 | "github.com/tj/go-spin" 9 | ) 10 | 11 | // Spinner creates a new spinner. The created spinner should NOT be started 12 | // or data races will occur that can result in a panic. 13 | func Spinner() *SpinnerComponent { 14 | // Create our spinner and setup our default frames 15 | s := spin.New() 16 | s.Set(spin.Default) 17 | 18 | return &SpinnerComponent{ 19 | s: s, 20 | } 21 | } 22 | 23 | type SpinnerComponent struct { 24 | s *spin.Spinner 25 | last time.Time 26 | } 27 | 28 | func (c *SpinnerComponent) Body(context.Context) glint.Component { 29 | current := time.Now() 30 | if c.last.IsZero() || current.Sub(c.last) > 150*time.Millisecond { 31 | c.last = current 32 | c.s.Next() 33 | } 34 | 35 | return glint.Text(c.s.Current()) 36 | } 37 | -------------------------------------------------------------------------------- /components/stopwatch.go: -------------------------------------------------------------------------------- 1 | package components 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/mitchellh/go-glint" 8 | ) 9 | 10 | // Stopwatch creates a new stopwatch component that starts at the given time. 11 | func Stopwatch(start time.Time) *StopwatchComponent { 12 | return &StopwatchComponent{ 13 | start: start, 14 | } 15 | } 16 | 17 | type StopwatchComponent struct { 18 | start time.Time 19 | } 20 | 21 | func (c *StopwatchComponent) Body(context.Context) glint.Component { 22 | return glint.Text(time.Now().Sub(c.start).Truncate(100 * time.Millisecond).String()) 23 | } 24 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | package glint 2 | 3 | // Context is a component type that can be used to set data on the context 4 | // given to Body calls for components that are children of this component. 5 | func Context(inner Component, kv ...interface{}) Component { 6 | if len(kv)%2 != 0 { 7 | panic("kv must be set in pairs") 8 | } 9 | 10 | return &contextComponent{inner: inner, pairs: kv} 11 | } 12 | 13 | type contextComponent struct { 14 | terminalComponent 15 | 16 | inner Component 17 | pairs []interface{} 18 | } 19 | -------------------------------------------------------------------------------- /document.go: -------------------------------------------------------------------------------- 1 | package glint 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "os" 7 | "sync" 8 | "time" 9 | 10 | "github.com/mitchellh/go-glint/flex" 11 | ) 12 | 13 | // Document is the primary structure for managing and drawing components. 14 | // 15 | // A document represents a terminal window or session. The output can be set and 16 | // components can be added, rendered, and drawn. All the methods on a Document 17 | // are thread-safe unless otherwise documented. This allows you to draw, 18 | // add components, replace components, etc. all while the render loop is active. 19 | // 20 | // Currently, this can only render directly to an io.Writer that expects to 21 | // be a terminal session. In the future, we'll further abstract the concept 22 | // of a "renderer" so that rendering can be done to other mediums as well. 23 | type Document struct { 24 | mu sync.Mutex 25 | r Renderer 26 | els []Component 27 | refreshRate time.Duration 28 | prevRoot *flex.Node 29 | mounted map[ComponentMounter]struct{} 30 | paused bool 31 | closed bool 32 | } 33 | 34 | // New returns a Document that will output to stdout. 35 | func New() *Document { 36 | var d Document 37 | d.SetRenderer(&TerminalRenderer{ 38 | Output: os.Stdout, 39 | }) 40 | 41 | return &d 42 | } 43 | 44 | // SetRenderer sets the renderer to use. If this isn't set then Render 45 | // will do nothing and return immediately. Changes to this will have no 46 | // impact on active render loops. 47 | func (d *Document) SetRenderer(r Renderer) { 48 | d.mu.Lock() 49 | defer d.mu.Unlock() 50 | d.r = r 51 | } 52 | 53 | // SetRefreshRate sets the rate at which output is rendered. 54 | func (d *Document) SetRefreshRate(dur time.Duration) { 55 | d.mu.Lock() 56 | defer d.mu.Unlock() 57 | d.refreshRate = dur 58 | } 59 | 60 | // Append appends components to the document. 61 | func (d *Document) Append(el ...Component) { 62 | d.mu.Lock() 63 | defer d.mu.Unlock() 64 | d.els = append(d.els, el...) 65 | } 66 | 67 | // Set sets the components for the document. This will replace all 68 | // previous components. 69 | func (d *Document) Set(els ...Component) { 70 | d.mu.Lock() 71 | defer d.mu.Unlock() 72 | d.els = els 73 | } 74 | 75 | // Close ensures that all elements are unmounted by finalizing all the 76 | // output and then calling RenderFrame. Users of Document should ensure 77 | // that Close is always called. 78 | func (d *Document) Close() error { 79 | d.mu.Lock() 80 | if d.closed { 81 | d.mu.Unlock() 82 | return nil 83 | } 84 | 85 | for i, el := range d.els { 86 | d.els[i] = Finalize(el) 87 | } 88 | 89 | d.closed = true 90 | r := d.r 91 | d.mu.Unlock() 92 | 93 | // We call RenderFrame twice to ensure we remove the elements AND 94 | // call Unmount on them. 95 | d.RenderFrame() 96 | d.RenderFrame() 97 | 98 | // If our renderer implements closer then call close 99 | if c, ok := r.(io.Closer); ok { 100 | c.Close() 101 | } 102 | 103 | return nil 104 | } 105 | 106 | // Pause will pause the renderer. This will case RenderFrame to do nothing 107 | // until Resume is called. The use case for this is if you want to wait for 108 | // input (stdin) or any other reason. 109 | func (d *Document) Pause() { 110 | d.mu.Lock() 111 | defer d.mu.Unlock() 112 | d.paused = true 113 | } 114 | 115 | // Resume undoes a Pause call. If not paused, this does nothing. 116 | func (d *Document) Resume() { 117 | d.mu.Lock() 118 | defer d.mu.Unlock() 119 | d.paused = false 120 | } 121 | 122 | // Render starts a render loop that continues to render until the 123 | // context is cancelled. This will render at the configured refresh rate. 124 | // If the refresh rate is changed, it will not affect an active render loop. 125 | // You must cancel and restart the render loop. 126 | func (d *Document) Render(ctx context.Context) { 127 | d.mu.Lock() 128 | dur := d.refreshRate 129 | d.mu.Unlock() 130 | if dur == 0 { 131 | dur = time.Second / 24 132 | } 133 | 134 | for { 135 | // Render. We time the render so that we can adapt the framerate 136 | // if the render is taking too long. 137 | start := time.Now() 138 | d.RenderFrame() 139 | renderDur := time.Now().Sub(start) 140 | 141 | // If our context is canceled, end. 142 | if ctx.Err() != nil { 143 | return 144 | } 145 | 146 | // If the duration is greater than our goal duration, then we 147 | // adapt our duration. Otherwise, we sleep the duration we want 148 | // and continue 149 | sleepDur := dur 150 | if renderDur > dur { 151 | sleepDur = renderDur 152 | 153 | // We slow our attempted framerate down at most to 1 fps 154 | if sleepDur > 1*time.Second { 155 | sleepDur = 1 * time.Second 156 | } 157 | } 158 | 159 | time.Sleep(sleepDur) 160 | } 161 | } 162 | 163 | // RenderFrame will render a single frame and return. 164 | // 165 | // If a manual size is not configured, this will recalcualte the window 166 | // size on each call. This typically requires a syscall. This is a bit 167 | // expensive but something we can optimize in the future if it ends up being 168 | // a real source of FPS issues. 169 | func (d *Document) RenderFrame() { 170 | d.mu.Lock() 171 | defer d.mu.Unlock() 172 | 173 | // If we're paused do nothing. 174 | if d.paused { 175 | return 176 | } 177 | 178 | // If we don't have a renderer set, then don't render anything. 179 | if d.r == nil { 180 | return 181 | } 182 | 183 | // Our context 184 | ctx := WithRenderer(context.Background(), d.r) 185 | 186 | // Setup our root node 187 | root := d.r.LayoutRoot() 188 | if root == nil { 189 | return 190 | } 191 | 192 | // Build our render tree 193 | tree(ctx, root, Fragment(d.els...), false) 194 | 195 | // Calculate the layout 196 | flex.CalculateLayout(root, flex.Undefined, flex.Undefined, flex.DirectionLTR) 197 | 198 | // Fix any text nodes that need to be fixed. 199 | d.handleNodes(ctx, root, nil) 200 | 201 | // If the height of the root is zero then we do nothing. 202 | if uint(root.LayoutGetHeight()) == 0 { 203 | return 204 | } 205 | 206 | // Render the tree 207 | d.r.RenderRoot(root, d.prevRoot) 208 | 209 | // Store how much we drew 210 | height := uint(root.LayoutGetHeight()) 211 | 212 | // If our component list is prefixed with finalized components, we 213 | // prune these out and do not re-render them. 214 | finalIdx := -1 215 | for i, el := range d.els { 216 | child := root.GetChild(i) 217 | if child == nil { 218 | break 219 | } 220 | 221 | // If the component is not finalized then we exit. If the 222 | // component doesn't match our expectations it means we hit 223 | // something weird and we exit too. 224 | ctx, ok := child.Context.(*parentContext) 225 | if !ok || ctx == nil || ctx.C != el || !ctx.Finalized { 226 | break 227 | } 228 | 229 | // If this is finalized, then we have to subtract from the 230 | // height the height of this child since we're not going to redraw. 231 | // Then continue until we find one that isn't finalized. 232 | height -= uint(child.LayoutGetHeight()) 233 | finalIdx = i 234 | } 235 | if finalIdx >= 0 { 236 | // Change our elements 237 | els := d.els[finalIdx+1:] 238 | d.els = make([]Component, len(els)) 239 | copy(d.els, els) 240 | 241 | // Reset the height on the root so that it reflects this change 242 | root.Layout.Dimensions[flex.DimensionHeight] = float32(height) 243 | } 244 | 245 | // Store our previous root 246 | d.prevRoot = root 247 | } 248 | 249 | func (d *Document) handleNodes( 250 | ctx context.Context, 251 | parent *flex.Node, 252 | seen map[ComponentMounter]struct{}, 253 | ) { 254 | // For our first call, we detect the root since we use it later 255 | // to do some final calls. 256 | root := seen == nil 257 | if root { 258 | seen = map[ComponentMounter]struct{}{} 259 | } 260 | 261 | for _, child := range parent.Children { 262 | if tctx, ok := child.Context.(treeContext); ok { 263 | c := tctx.Component() 264 | 265 | // Mount callbacks 266 | if mc, ok := c.(ComponentMounter); ok { 267 | // Only if we haven't seen this already... 268 | if _, ok := seen[mc]; !ok { 269 | seen[mc] = struct{}{} 270 | 271 | if d.mounted == nil { 272 | d.mounted = map[ComponentMounter]struct{}{} 273 | } 274 | 275 | // And we haven't notified this already... 276 | if _, ok := d.mounted[mc]; !ok { 277 | d.mounted[mc] = struct{}{} 278 | 279 | // Notify 280 | mc.Mount(ctx) 281 | } 282 | } 283 | } 284 | 285 | continue 286 | } 287 | 288 | // If the height/width that the layout engine calculated is less than 289 | // the height that we originally measured, then we need to give the 290 | // element a chance to rerender into that dimension. 291 | if tctx, ok := child.Context.(*TextNodeContext); ok { 292 | height := child.LayoutGetHeight() 293 | width := child.LayoutGetWidth() 294 | if height < tctx.Size.Height || width < tctx.Size.Width { 295 | child.Measure(child, 296 | width, flex.MeasureModeAtMost, 297 | height, flex.MeasureModeAtMost, 298 | ) 299 | } 300 | } 301 | 302 | d.handleNodes(ctx, child, seen) 303 | } 304 | 305 | // If we're the root call, then we preform some final calls. Otherwise 306 | // we just return, we're done. 307 | if !root { 308 | return 309 | } 310 | 311 | // Go through our previously mounted set and if we didn't see it, 312 | // then call unmount on it. After we're done, what we saw is our new 313 | // map of mounted elements. 314 | for mc := range d.mounted { 315 | if _, ok := seen[mc]; !ok { 316 | mc.Unmount(ctx) 317 | } 318 | } 319 | d.mounted = seen 320 | } 321 | -------------------------------------------------------------------------------- /document_test.go: -------------------------------------------------------------------------------- 1 | package glint 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "sync/atomic" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestDocument_mountUnmount(t *testing.T) { 13 | require := require.New(t) 14 | 15 | // Create our doc 16 | r := &StringRenderer{} 17 | d := New() 18 | d.SetRenderer(r) 19 | 20 | // Add our component 21 | var c testMount 22 | d.Append(&c) 23 | require.Equal(uint32(0), atomic.LoadUint32(&c.mount)) 24 | require.Equal(uint32(0), atomic.LoadUint32(&c.unmount)) 25 | 26 | // Render once 27 | d.RenderFrame() 28 | require.Equal(uint32(1), atomic.LoadUint32(&c.mount)) 29 | require.Equal(uint32(0), atomic.LoadUint32(&c.unmount)) 30 | 31 | // Render again 32 | d.RenderFrame() 33 | require.Equal(uint32(1), atomic.LoadUint32(&c.mount)) 34 | require.Equal(uint32(0), atomic.LoadUint32(&c.unmount)) 35 | 36 | // Remove the old components 37 | d.Set() 38 | d.RenderFrame() 39 | require.Equal(uint32(1), atomic.LoadUint32(&c.mount)) 40 | require.Equal(uint32(1), atomic.LoadUint32(&c.unmount)) 41 | 42 | // Render again 43 | d.RenderFrame() 44 | require.Equal(uint32(1), atomic.LoadUint32(&c.mount)) 45 | require.Equal(uint32(1), atomic.LoadUint32(&c.unmount)) 46 | } 47 | 48 | func TestDocument_unmountClose(t *testing.T) { 49 | require := require.New(t) 50 | 51 | // Create our doc 52 | r := &StringRenderer{} 53 | d := New() 54 | d.SetRenderer(r) 55 | 56 | // Add our component 57 | var c testMount 58 | d.Append(&c) 59 | require.Equal(uint32(0), atomic.LoadUint32(&c.mount)) 60 | require.Equal(uint32(0), atomic.LoadUint32(&c.unmount)) 61 | 62 | // Render once 63 | d.RenderFrame() 64 | require.Equal(uint32(1), atomic.LoadUint32(&c.mount)) 65 | require.Equal(uint32(0), atomic.LoadUint32(&c.unmount)) 66 | 67 | // Render again 68 | require.NoError(d.Close()) 69 | require.Equal(uint32(1), atomic.LoadUint32(&c.mount)) 70 | require.Equal(uint32(1), atomic.LoadUint32(&c.unmount)) 71 | } 72 | 73 | func TestDocument_renderingWithoutLayout(t *testing.T) { 74 | var buf bytes.Buffer 75 | 76 | d := New() 77 | d.SetRenderer(&TerminalRenderer{ 78 | Output: &buf, 79 | }) 80 | 81 | var c testMount 82 | d.Append(&c) 83 | 84 | // Render once 85 | d.RenderFrame() 86 | require.Empty(t, buf.String()) 87 | require.Zero(t, atomic.LoadUint32(&c.mount)) 88 | } 89 | 90 | type testMount struct { 91 | terminalComponent 92 | 93 | mount uint32 94 | unmount uint32 95 | } 96 | 97 | func (c *testMount) Mount(context.Context) { atomic.AddUint32(&c.mount, 1) } 98 | func (c *testMount) Unmount(context.Context) { atomic.AddUint32(&c.unmount, 1) } 99 | 100 | var ( 101 | _ Component = (*testMount)(nil) 102 | _ ComponentMounter = (*testMount)(nil) 103 | ) 104 | -------------------------------------------------------------------------------- /examples/counter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/mitchellh/go-glint" 10 | ) 11 | 12 | func main() { 13 | var counter uint32 14 | go func() { 15 | for { 16 | time.Sleep(100 * time.Millisecond) 17 | atomic.AddUint32(&counter, 1) 18 | } 19 | }() 20 | 21 | d := glint.New() 22 | d.Append( 23 | glint.Style( 24 | glint.TextFunc(func(rows, cols uint) string { 25 | return fmt.Sprintf("%d tests passed", atomic.LoadUint32(&counter)) 26 | }), 27 | glint.Color("green"), 28 | ), 29 | ) 30 | d.Render(context.Background()) 31 | } 32 | -------------------------------------------------------------------------------- /examples/layout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/mitchellh/go-glint" 8 | gc "github.com/mitchellh/go-glint/components" 9 | ) 10 | 11 | func main() { 12 | d := glint.New() 13 | d.Append( 14 | glint.Style( 15 | glint.Layout( 16 | gc.Spinner(), 17 | glint.Layout(glint.Text("Build site and validate links...")).MarginLeft(1), 18 | glint.Layout(gc.Stopwatch(time.Now())).MarginLeft(1), 19 | ).Row(), 20 | glint.Color("green"), 21 | ), 22 | glint.Layout( 23 | gc.Spinner(), 24 | glint.Layout(glint.Text("Preparing execution environment...")).MarginLeft(1), 25 | glint.Layout(gc.Stopwatch(time.Now())).MarginLeft(1), 26 | ).MarginLeft(2).Row(), 27 | glint.Layout( 28 | glint.Text("Preparing volume to work with..."), 29 | ).MarginLeft(4), 30 | glint.Text("\nWaiting..."), 31 | ) 32 | d.Render(context.Background()) 33 | } 34 | -------------------------------------------------------------------------------- /examples/sparkline.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "math/rand" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/mitchellh/go-glint" 11 | gc "github.com/mitchellh/go-glint/components" 12 | ) 13 | 14 | func main() { 15 | max := 25 16 | min := 1 17 | 18 | values := make([]uint, 40) 19 | for i := range values { 20 | values[i] = uint(rand.Intn(max-min) + min) 21 | } 22 | 23 | // Create our sparkline 24 | sl := gc.Sparkline(values) 25 | sl.PeakStyle = []glint.StyleOption{glint.Color("green")} 26 | 27 | // Start up a timer that adds values 28 | lastValue := uint32(values[len(values)-1]) 29 | go func() { 30 | for { 31 | time.Sleep(100 * time.Millisecond) 32 | value := uint(rand.Intn(max-min) + min) 33 | sl.Append(value) 34 | atomic.StoreUint32(&lastValue, uint32(value)) 35 | } 36 | }() 37 | 38 | d := glint.New() 39 | d.Append( 40 | glint.Layout( 41 | glint.Layout(glint.Text("requests/second")).MarginRight(2), 42 | glint.Layout(sl).MarginRight(2), 43 | glint.TextFunc(func(h, w uint) string { 44 | return fmt.Sprintf("%d req/s", atomic.LoadUint32(&lastValue)) 45 | }), 46 | ).Row(), 47 | ) 48 | d.Render(context.Background()) 49 | } 50 | -------------------------------------------------------------------------------- /finalize.go: -------------------------------------------------------------------------------- 1 | package glint 2 | 3 | import "context" 4 | 5 | // Finalize reutrns a component that will finalize the input component. 6 | // See ComponentFinalizer for documentation on what finalization means. 7 | func Finalize(c Component) Component { 8 | return &finalizedComponent{ 9 | Component: c, 10 | } 11 | } 12 | 13 | type finalizedComponent struct { 14 | Component 15 | } 16 | 17 | func (c *finalizedComponent) Body(context.Context) Component { 18 | return c.Component 19 | } 20 | -------------------------------------------------------------------------------- /flex/LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For yoga software 4 | 5 | Copyright (c) 2014-present, Facebook, Inc. All rights reserved. 6 | Copyright (c) 2017-present, Krzysztof Kowalczyk. All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | * Neither the name Facebook nor the names of its contributors may be used to 19 | endorse or promote products derived from this software without specific 20 | prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 29 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /flex/PATENTS: -------------------------------------------------------------------------------- 1 | Additional Grant of Patent Rights Version 2 2 | 3 | "Software" means the yoga software distributed by Facebook, Inc. 4 | 5 | Facebook, Inc. (“Facebook”) hereby grants to each recipient of the Software 6 | (“you”) a perpetual, worldwide, royalty-free, non-exclusive, irrevocable 7 | (subject to the termination provision below) license under any Necessary 8 | Claims, to make, have made, use, sell, offer to sell, import, and otherwise 9 | transfer the Software. For avoidance of doubt, no license is granted under 10 | Facebook's rights in any patent claims that are infringed by (i) modifications 11 | to the Software made by you or any third party or (ii) the Software in 12 | combination with any software or other technology. 13 | 14 | The license granted hereunder will terminate, automatically and without notice, 15 | if you (or any of your subsidiaries, corporate affiliates or agents) initiate 16 | directly or indirectly, or take a direct financial interest in, any Patent 17 | Assertion: (i) against Facebook or any of its subsidiaries or corporate 18 | affiliates, (ii) against any party if such Patent Assertion arises in whole or 19 | in part from any software, technology, product or service of Facebook or any of 20 | its subsidiaries or corporate affiliates, or (iii) against any party relating 21 | to the Software. Notwithstanding the foregoing, if Facebook or any of its 22 | subsidiaries or corporate affiliates files a lawsuit alleging patent 23 | infringement against you in the first instance, and you respond by filing a 24 | patent infringement counterclaim in that lawsuit against that party that is 25 | unrelated to the Software, the license granted hereunder will not terminate 26 | under section (i) of this paragraph due to such counterclaim. 27 | 28 | A "Necessary Claim" is a claim of a patent owned by Facebook that is 29 | necessarily infringed by the Software standing alone. 30 | 31 | A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, 32 | or contributory infringement or inducement to infringe any patent, including a 33 | cross-claim or counterclaim. -------------------------------------------------------------------------------- /flex/README.md: -------------------------------------------------------------------------------- 1 | # flex - CSS flexbox layout implementation in Go 2 | 3 | Go implementation of [flexbox CSS](https://www.w3.org/TR/css-flexbox-1/) layout algorithm. 4 | 5 | A pure Go port of [Facebook's Yoga](https://github.com/facebook/yoga). 6 | 7 | ## How to use 8 | 9 | Read [tutorial](https://blog.kowalczyk.info/article/9/tutorial-on-using-github.comkjkflex-go-package.html) or look at `_test.go` files. 10 | 11 | ## Status 12 | 13 | The port is finished. The code works and passess all Yoga tests. 14 | 15 | The API is awkward by Go standards but it's the best I could do given that I want to stay close to C version. 16 | 17 | Logic is currently synced up to https://github.com/facebook/yoga/commit/f45059e1e696727c1282742b89d2c8bf06345254 18 | 19 | ## How the port was made 20 | 21 | You can read a [detailed story](https://blog.kowalczyk.info/article/wN9R/experience-porting-4.5k-loc-of-c-to-go-facebooks-css-flexbox-implementation-yoga.html). 22 | 23 | In short: 24 | 25 | * manually ported [C code](https://github.com/facebook/yoga/tree/master/yoga) to Go, line-by-line 26 | * manually ported [tests](https://github.com/facebook/yoga/tree/master/tests) to Go 27 | * tweak the API from C style to be more Go like. The structure and logic still is very close to C code (this makes porting future C changes easy) 28 | -------------------------------------------------------------------------------- /flex/align_self_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestAlign_self_center(t *testing.T) { 6 | config := NewConfig() 7 | 8 | root := NewNodeWithConfig(config) 9 | root.StyleSetWidth(100) 10 | root.StyleSetHeight(100) 11 | 12 | rootChild0 := NewNodeWithConfig(config) 13 | rootChild0.StyleSetAlignSelf(AlignCenter) 14 | rootChild0.StyleSetWidth(10) 15 | rootChild0.StyleSetHeight(10) 16 | root.InsertChild(rootChild0, 0) 17 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 18 | 19 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 20 | assertFloatEqual(t, 0, root.LayoutGetTop()) 21 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 22 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 23 | 24 | assertFloatEqual(t, 45, rootChild0.LayoutGetLeft()) 25 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 26 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 27 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 28 | 29 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 30 | 31 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 32 | assertFloatEqual(t, 0, root.LayoutGetTop()) 33 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 34 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 35 | 36 | assertFloatEqual(t, 45, rootChild0.LayoutGetLeft()) 37 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 38 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 39 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 40 | } 41 | 42 | func TestAlign_self_flex_end(t *testing.T) { 43 | config := NewConfig() 44 | 45 | root := NewNodeWithConfig(config) 46 | root.StyleSetWidth(100) 47 | root.StyleSetHeight(100) 48 | 49 | rootChild0 := NewNodeWithConfig(config) 50 | rootChild0.StyleSetAlignSelf(AlignFlexEnd) 51 | rootChild0.StyleSetWidth(10) 52 | rootChild0.StyleSetHeight(10) 53 | root.InsertChild(rootChild0, 0) 54 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 55 | 56 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 57 | assertFloatEqual(t, 0, root.LayoutGetTop()) 58 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 59 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 60 | 61 | assertFloatEqual(t, 90, rootChild0.LayoutGetLeft()) 62 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 63 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 64 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 65 | 66 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 67 | 68 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 69 | assertFloatEqual(t, 0, root.LayoutGetTop()) 70 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 71 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 72 | 73 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 74 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 75 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 76 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 77 | } 78 | 79 | func TestAlign_self_flex_start(t *testing.T) { 80 | config := NewConfig() 81 | 82 | root := NewNodeWithConfig(config) 83 | root.StyleSetWidth(100) 84 | root.StyleSetHeight(100) 85 | 86 | rootChild0 := NewNodeWithConfig(config) 87 | rootChild0.StyleSetAlignSelf(AlignFlexStart) 88 | rootChild0.StyleSetWidth(10) 89 | rootChild0.StyleSetHeight(10) 90 | root.InsertChild(rootChild0, 0) 91 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 92 | 93 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 94 | assertFloatEqual(t, 0, root.LayoutGetTop()) 95 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 96 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 97 | 98 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 99 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 100 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 101 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 102 | 103 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 104 | 105 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 106 | assertFloatEqual(t, 0, root.LayoutGetTop()) 107 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 108 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 109 | 110 | assertFloatEqual(t, 90, rootChild0.LayoutGetLeft()) 111 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 112 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 113 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 114 | } 115 | 116 | func TestAlign_self_flex_end_override_flex_start(t *testing.T) { 117 | config := NewConfig() 118 | 119 | root := NewNodeWithConfig(config) 120 | root.StyleSetAlignItems(AlignFlexStart) 121 | root.StyleSetWidth(100) 122 | root.StyleSetHeight(100) 123 | 124 | rootChild0 := NewNodeWithConfig(config) 125 | rootChild0.StyleSetAlignSelf(AlignFlexEnd) 126 | rootChild0.StyleSetWidth(10) 127 | rootChild0.StyleSetHeight(10) 128 | root.InsertChild(rootChild0, 0) 129 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 130 | 131 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 132 | assertFloatEqual(t, 0, root.LayoutGetTop()) 133 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 134 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 135 | 136 | assertFloatEqual(t, 90, rootChild0.LayoutGetLeft()) 137 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 138 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 139 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 140 | 141 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 142 | 143 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 144 | assertFloatEqual(t, 0, root.LayoutGetTop()) 145 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 146 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 147 | 148 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 149 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 150 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 151 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 152 | } 153 | 154 | func TestAlign_self_baseline(t *testing.T) { 155 | config := NewConfig() 156 | 157 | root := NewNodeWithConfig(config) 158 | root.StyleSetFlexDirection(FlexDirectionRow) 159 | root.StyleSetWidth(100) 160 | root.StyleSetHeight(100) 161 | 162 | rootChild0 := NewNodeWithConfig(config) 163 | rootChild0.StyleSetAlignSelf(AlignBaseline) 164 | rootChild0.StyleSetWidth(50) 165 | rootChild0.StyleSetHeight(50) 166 | root.InsertChild(rootChild0, 0) 167 | 168 | rootChild1 := NewNodeWithConfig(config) 169 | rootChild1.StyleSetAlignSelf(AlignBaseline) 170 | rootChild1.StyleSetWidth(50) 171 | rootChild1.StyleSetHeight(20) 172 | root.InsertChild(rootChild1, 1) 173 | 174 | rootChild1child0 := NewNodeWithConfig(config) 175 | rootChild1child0.StyleSetWidth(50) 176 | rootChild1child0.StyleSetHeight(10) 177 | rootChild1.InsertChild(rootChild1child0, 0) 178 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 179 | 180 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 181 | assertFloatEqual(t, 0, root.LayoutGetTop()) 182 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 183 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 184 | 185 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 186 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 187 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 188 | assertFloatEqual(t, 50, rootChild0.LayoutGetHeight()) 189 | 190 | assertFloatEqual(t, 50, rootChild1.LayoutGetLeft()) 191 | assertFloatEqual(t, 40, rootChild1.LayoutGetTop()) 192 | assertFloatEqual(t, 50, rootChild1.LayoutGetWidth()) 193 | assertFloatEqual(t, 20, rootChild1.LayoutGetHeight()) 194 | 195 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetLeft()) 196 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetTop()) 197 | assertFloatEqual(t, 50, rootChild1child0.LayoutGetWidth()) 198 | assertFloatEqual(t, 10, rootChild1child0.LayoutGetHeight()) 199 | 200 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 201 | 202 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 203 | assertFloatEqual(t, 0, root.LayoutGetTop()) 204 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 205 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 206 | 207 | assertFloatEqual(t, 50, rootChild0.LayoutGetLeft()) 208 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 209 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 210 | assertFloatEqual(t, 50, rootChild0.LayoutGetHeight()) 211 | 212 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 213 | assertFloatEqual(t, 40, rootChild1.LayoutGetTop()) 214 | assertFloatEqual(t, 50, rootChild1.LayoutGetWidth()) 215 | assertFloatEqual(t, 20, rootChild1.LayoutGetHeight()) 216 | 217 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetLeft()) 218 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetTop()) 219 | assertFloatEqual(t, 50, rootChild1child0.LayoutGetWidth()) 220 | assertFloatEqual(t, 10, rootChild1child0.LayoutGetHeight()) 221 | } 222 | -------------------------------------------------------------------------------- /flex/baseline_func_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func baselineFunc(node *Node, width float32, height float32) float32 { 6 | baseline := node.Context.(float32) 7 | return baseline 8 | } 9 | 10 | func TestAlign_baseline_customer_func(t *testing.T) { 11 | root := NewNode() 12 | root.StyleSetFlexDirection(FlexDirectionRow) 13 | root.StyleSetAlignItems(AlignBaseline) 14 | root.StyleSetWidth(100) 15 | root.StyleSetHeight(100) 16 | 17 | rootChild0 := NewNode() 18 | rootChild0.StyleSetWidth(50) 19 | rootChild0.StyleSetHeight(50) 20 | root.InsertChild(rootChild0, 0) 21 | 22 | rootChild1 := NewNode() 23 | rootChild1.StyleSetWidth(50) 24 | rootChild1.StyleSetHeight(20) 25 | root.InsertChild(rootChild1, 1) 26 | 27 | var baselineValue float32 = 10 28 | rootChild1child0 := NewNode() 29 | rootChild1child0.Context = baselineValue 30 | rootChild1child0.StyleSetWidth(50) 31 | rootChild1child0.Baseline = baselineFunc 32 | rootChild1child0.StyleSetHeight(20) 33 | rootChild1.InsertChild(rootChild1child0, 0) 34 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 35 | 36 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 37 | assertFloatEqual(t, 0, root.LayoutGetTop()) 38 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 39 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 40 | 41 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 42 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 43 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 44 | assertFloatEqual(t, 50, rootChild0.LayoutGetHeight()) 45 | 46 | assertFloatEqual(t, 50, rootChild1.LayoutGetLeft()) 47 | assertFloatEqual(t, 40, rootChild1.LayoutGetTop()) 48 | assertFloatEqual(t, 50, rootChild1.LayoutGetWidth()) 49 | assertFloatEqual(t, 20, rootChild1.LayoutGetHeight()) 50 | 51 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetLeft()) 52 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetTop()) 53 | assertFloatEqual(t, 50, rootChild1child0.LayoutGetWidth()) 54 | assertFloatEqual(t, 20, rootChild1child0.LayoutGetHeight()) 55 | } 56 | -------------------------------------------------------------------------------- /flex/border_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestBorder_no_size(t *testing.T) { 6 | config := NewConfig() 7 | 8 | root := NewNodeWithConfig(config) 9 | root.StyleSetBorder(EdgeLeft, 10) 10 | root.StyleSetBorder(EdgeTop, 10) 11 | root.StyleSetBorder(EdgeRight, 10) 12 | root.StyleSetBorder(EdgeBottom, 10) 13 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 14 | 15 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 16 | assertFloatEqual(t, 0, root.LayoutGetTop()) 17 | assertFloatEqual(t, 20, root.LayoutGetWidth()) 18 | assertFloatEqual(t, 20, root.LayoutGetHeight()) 19 | 20 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 21 | 22 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 23 | assertFloatEqual(t, 0, root.LayoutGetTop()) 24 | assertFloatEqual(t, 20, root.LayoutGetWidth()) 25 | assertFloatEqual(t, 20, root.LayoutGetHeight()) 26 | } 27 | 28 | func TestBorder_container_match_child(t *testing.T) { 29 | config := NewConfig() 30 | 31 | root := NewNodeWithConfig(config) 32 | root.StyleSetBorder(EdgeLeft, 10) 33 | root.StyleSetBorder(EdgeTop, 10) 34 | root.StyleSetBorder(EdgeRight, 10) 35 | root.StyleSetBorder(EdgeBottom, 10) 36 | 37 | rootChild0 := NewNodeWithConfig(config) 38 | rootChild0.StyleSetWidth(10) 39 | rootChild0.StyleSetHeight(10) 40 | root.InsertChild(rootChild0, 0) 41 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 42 | 43 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 44 | assertFloatEqual(t, 0, root.LayoutGetTop()) 45 | assertFloatEqual(t, 30, root.LayoutGetWidth()) 46 | assertFloatEqual(t, 30, root.LayoutGetHeight()) 47 | 48 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 49 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 50 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 51 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 52 | 53 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 54 | 55 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 56 | assertFloatEqual(t, 0, root.LayoutGetTop()) 57 | assertFloatEqual(t, 30, root.LayoutGetWidth()) 58 | assertFloatEqual(t, 30, root.LayoutGetHeight()) 59 | 60 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 61 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 62 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 63 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 64 | } 65 | 66 | func TestBorder_flex_child(t *testing.T) { 67 | config := NewConfig() 68 | 69 | root := NewNodeWithConfig(config) 70 | root.StyleSetBorder(EdgeLeft, 10) 71 | root.StyleSetBorder(EdgeTop, 10) 72 | root.StyleSetBorder(EdgeRight, 10) 73 | root.StyleSetBorder(EdgeBottom, 10) 74 | root.StyleSetWidth(100) 75 | root.StyleSetHeight(100) 76 | 77 | rootChild0 := NewNodeWithConfig(config) 78 | rootChild0.StyleSetFlexGrow(1) 79 | rootChild0.StyleSetWidth(10) 80 | root.InsertChild(rootChild0, 0) 81 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 82 | 83 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 84 | assertFloatEqual(t, 0, root.LayoutGetTop()) 85 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 86 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 87 | 88 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 89 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 90 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 91 | assertFloatEqual(t, 80, rootChild0.LayoutGetHeight()) 92 | 93 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 94 | 95 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 96 | assertFloatEqual(t, 0, root.LayoutGetTop()) 97 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 98 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 99 | 100 | assertFloatEqual(t, 80, rootChild0.LayoutGetLeft()) 101 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 102 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 103 | assertFloatEqual(t, 80, rootChild0.LayoutGetHeight()) 104 | } 105 | 106 | func TestBorder_stretch_child(t *testing.T) { 107 | config := NewConfig() 108 | 109 | root := NewNodeWithConfig(config) 110 | root.StyleSetBorder(EdgeLeft, 10) 111 | root.StyleSetBorder(EdgeTop, 10) 112 | root.StyleSetBorder(EdgeRight, 10) 113 | root.StyleSetBorder(EdgeBottom, 10) 114 | root.StyleSetWidth(100) 115 | root.StyleSetHeight(100) 116 | 117 | rootChild0 := NewNodeWithConfig(config) 118 | rootChild0.StyleSetHeight(10) 119 | root.InsertChild(rootChild0, 0) 120 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 121 | 122 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 123 | assertFloatEqual(t, 0, root.LayoutGetTop()) 124 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 125 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 126 | 127 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 128 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 129 | assertFloatEqual(t, 80, rootChild0.LayoutGetWidth()) 130 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 131 | 132 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 133 | 134 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 135 | assertFloatEqual(t, 0, root.LayoutGetTop()) 136 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 137 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 138 | 139 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 140 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 141 | assertFloatEqual(t, 80, rootChild0.LayoutGetWidth()) 142 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 143 | } 144 | 145 | func TestBorder_center_child(t *testing.T) { 146 | config := NewConfig() 147 | 148 | root := NewNodeWithConfig(config) 149 | root.StyleSetJustifyContent(JustifyCenter) 150 | root.StyleSetAlignItems(AlignCenter) 151 | root.StyleSetBorder(EdgeStart, 10) 152 | root.StyleSetBorder(EdgeEnd, 20) 153 | root.StyleSetBorder(EdgeBottom, 20) 154 | root.StyleSetWidth(100) 155 | root.StyleSetHeight(100) 156 | 157 | rootChild0 := NewNodeWithConfig(config) 158 | rootChild0.StyleSetWidth(10) 159 | rootChild0.StyleSetHeight(10) 160 | root.InsertChild(rootChild0, 0) 161 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 162 | 163 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 164 | assertFloatEqual(t, 0, root.LayoutGetTop()) 165 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 166 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 167 | 168 | assertFloatEqual(t, 40, rootChild0.LayoutGetLeft()) 169 | assertFloatEqual(t, 35, rootChild0.LayoutGetTop()) 170 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 171 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 172 | 173 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 174 | 175 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 176 | assertFloatEqual(t, 0, root.LayoutGetTop()) 177 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 178 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 179 | 180 | assertFloatEqual(t, 50, rootChild0.LayoutGetLeft()) 181 | assertFloatEqual(t, 35, rootChild0.LayoutGetTop()) 182 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 183 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 184 | } 185 | -------------------------------------------------------------------------------- /flex/compute_margin_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestComputed_layout_margin(t *testing.T) { 6 | root := NewNode() 7 | root.StyleSetWidth(100) 8 | root.StyleSetHeight(100) 9 | root.StyleSetMarginPercent(EdgeStart, 10) 10 | 11 | CalculateLayout(root, 100, 100, DirectionLTR) 12 | 13 | assertFloatEqual(t, 10, root.LayoutGetMargin(EdgeLeft)) 14 | assertFloatEqual(t, 0, root.LayoutGetMargin(EdgeRight)) 15 | 16 | CalculateLayout(root, 100, 100, DirectionRTL) 17 | 18 | assertFloatEqual(t, 0, root.LayoutGetMargin(EdgeLeft)) 19 | assertFloatEqual(t, 10, root.LayoutGetMargin(EdgeRight)) 20 | } 21 | -------------------------------------------------------------------------------- /flex/compute_padding_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestComputed_layout_padding(t *testing.T) { 6 | root := NewNode() 7 | root.StyleSetWidth(100) 8 | root.StyleSetHeight(100) 9 | root.StyleSetPaddingPercent(EdgeStart, 10) 10 | 11 | CalculateLayout(root, 100, 100, DirectionLTR) 12 | 13 | assertFloatEqual(t, 10, root.LayoutGetPadding(EdgeLeft)) 14 | assertFloatEqual(t, 0, root.LayoutGetPadding(EdgeRight)) 15 | 16 | CalculateLayout(root, 100, 100, DirectionRTL) 17 | 18 | assertFloatEqual(t, 0, root.LayoutGetPadding(EdgeLeft)) 19 | assertFloatEqual(t, 10, root.LayoutGetPadding(EdgeRight)) 20 | } 21 | -------------------------------------------------------------------------------- /flex/default_values_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestAssert_default_values(t *testing.T) { 10 | root := NewNode() 11 | 12 | assert.Equal(t, 0, len(root.Children)) 13 | var nilNode *Node 14 | assert.Equal(t, nilNode, root.GetChild(1)) 15 | assert.Equal(t, nilNode, root.GetChild(0)) 16 | 17 | assert.Equal(t, DirectionInherit, root.Style.Direction) 18 | assert.Equal(t, FlexDirectionColumn, root.Style.FlexDirection) 19 | assert.Equal(t, JustifyFlexStart, root.Style.JustifyContent) 20 | assert.Equal(t, AlignFlexStart, root.Style.AlignContent) 21 | assert.Equal(t, AlignStretch, root.Style.AlignItems) 22 | assert.Equal(t, AlignAuto, root.Style.AlignSelf) 23 | assert.Equal(t, PositionTypeRelative, root.Style.PositionType) 24 | assert.Equal(t, WrapNoWrap, root.Style.FlexWrap) 25 | assert.Equal(t, OverflowVisible, root.Style.Overflow) 26 | assertFloatEqual(t, 0, root.StyleGetFlexGrow()) 27 | assertFloatEqual(t, 0, root.StyleGetFlexShrink()) 28 | assert.Equal(t, root.Style.FlexBasis.Unit, UnitAuto) 29 | 30 | assert.Equal(t, root.StyleGetPosition(EdgeLeft).Unit, UnitUndefined) 31 | assert.Equal(t, root.StyleGetPosition(EdgeTop).Unit, UnitUndefined) 32 | assert.Equal(t, root.StyleGetPosition(EdgeRight).Unit, UnitUndefined) 33 | assert.Equal(t, root.StyleGetPosition(EdgeBottom).Unit, UnitUndefined) 34 | assert.Equal(t, root.StyleGetPosition(EdgeStart).Unit, UnitUndefined) 35 | assert.Equal(t, root.StyleGetPosition(EdgeEnd).Unit, UnitUndefined) 36 | 37 | assert.Equal(t, root.StyleGetMargin(EdgeLeft).Unit, UnitUndefined) 38 | assert.Equal(t, root.StyleGetMargin(EdgeTop).Unit, UnitUndefined) 39 | assert.Equal(t, root.StyleGetMargin(EdgeRight).Unit, UnitUndefined) 40 | assert.Equal(t, root.StyleGetMargin(EdgeBottom).Unit, UnitUndefined) 41 | assert.Equal(t, root.StyleGetMargin(EdgeStart).Unit, UnitUndefined) 42 | assert.Equal(t, root.StyleGetMargin(EdgeEnd).Unit, UnitUndefined) 43 | 44 | assert.Equal(t, root.StyleGetPadding(EdgeLeft).Unit, UnitUndefined) 45 | assert.Equal(t, root.StyleGetPadding(EdgeTop).Unit, UnitUndefined) 46 | assert.Equal(t, root.StyleGetPadding(EdgeRight).Unit, UnitUndefined) 47 | assert.Equal(t, root.StyleGetPadding(EdgeBottom).Unit, UnitUndefined) 48 | assert.Equal(t, root.StyleGetPadding(EdgeStart).Unit, UnitUndefined) 49 | assert.Equal(t, root.StyleGetPadding(EdgeEnd).Unit, UnitUndefined) 50 | 51 | assert.True(t, FloatIsUndefined(root.StyleGetBorder(EdgeLeft))) 52 | assert.True(t, FloatIsUndefined(root.StyleGetBorder(EdgeTop))) 53 | assert.True(t, FloatIsUndefined(root.StyleGetBorder(EdgeRight))) 54 | assert.True(t, FloatIsUndefined(root.StyleGetBorder(EdgeBottom))) 55 | assert.True(t, FloatIsUndefined(root.StyleGetBorder(EdgeStart))) 56 | assert.True(t, FloatIsUndefined(root.StyleGetBorder(EdgeEnd))) 57 | 58 | assert.Equal(t, root.StyleGetWidth().Unit, UnitAuto) 59 | assert.Equal(t, root.StyleGetHeight().Unit, UnitAuto) 60 | assert.Equal(t, root.StyleGetMinWidth().Unit, UnitUndefined) 61 | assert.Equal(t, root.StyleGetMinHeight().Unit, UnitUndefined) 62 | assert.Equal(t, root.StyleGetMaxWidth().Unit, UnitUndefined) 63 | assert.Equal(t, root.StyleGetMaxHeight().Unit, UnitUndefined) 64 | 65 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 66 | assertFloatEqual(t, 0, root.LayoutGetTop()) 67 | assertFloatEqual(t, 0, root.LayoutGetRight()) 68 | assertFloatEqual(t, 0, root.LayoutGetBottom()) 69 | 70 | assertFloatEqual(t, 0, root.LayoutGetMargin(EdgeLeft)) 71 | assertFloatEqual(t, 0, root.LayoutGetMargin(EdgeTop)) 72 | assertFloatEqual(t, 0, root.LayoutGetMargin(EdgeRight)) 73 | assertFloatEqual(t, 0, root.LayoutGetMargin(EdgeBottom)) 74 | 75 | assertFloatEqual(t, 0, root.LayoutGetPadding(EdgeLeft)) 76 | assertFloatEqual(t, 0, root.LayoutGetPadding(EdgeTop)) 77 | assertFloatEqual(t, 0, root.LayoutGetPadding(EdgeRight)) 78 | assertFloatEqual(t, 0, root.LayoutGetPadding(EdgeBottom)) 79 | 80 | assertFloatEqual(t, 0, root.LayoutGetBorder(EdgeLeft)) 81 | assertFloatEqual(t, 0, root.LayoutGetBorder(EdgeTop)) 82 | assertFloatEqual(t, 0, root.LayoutGetBorder(EdgeRight)) 83 | assertFloatEqual(t, 0, root.LayoutGetBorder(EdgeBottom)) 84 | 85 | assert.True(t, FloatIsUndefined(root.LayoutGetWidth())) 86 | assert.True(t, FloatIsUndefined(root.LayoutGetHeight())) 87 | assert.Equal(t, DirectionInherit, root.Layout.Direction) 88 | 89 | } 90 | 91 | func TestAssert_webdefault_values(t *testing.T) { 92 | config := NewConfig() 93 | config.UseWebDefaults = true 94 | root := NewNodeWithConfig(config) 95 | 96 | assert.Equal(t, FlexDirectionRow, root.Style.FlexDirection) 97 | assert.Equal(t, AlignStretch, root.Style.AlignContent) 98 | assertFloatEqual(t, 1, root.StyleGetFlexShrink()) 99 | 100 | } 101 | 102 | func TestAssert_webdefault_values_reset(t *testing.T) { 103 | config := NewConfig() 104 | config.UseWebDefaults = true 105 | root := NewNodeWithConfig(config) 106 | root.Reset() 107 | 108 | assert.Equal(t, FlexDirectionRow, root.Style.FlexDirection) 109 | assert.Equal(t, AlignStretch, root.Style.AlignContent) 110 | assertFloatEqual(t, 1, root.StyleGetFlexShrink()) 111 | 112 | } 113 | -------------------------------------------------------------------------------- /flex/dimension_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestWrap_child(t *testing.T) { 6 | config := NewConfig() 7 | root := NewNodeWithConfig(config) 8 | 9 | rootChild0 := NewNodeWithConfig(config) 10 | rootChild0.StyleSetWidth(100) 11 | rootChild0.StyleSetHeight(100) 12 | root.InsertChild(rootChild0, 0) 13 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 14 | 15 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 16 | assertFloatEqual(t, 0, root.LayoutGetTop()) 17 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 18 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 19 | 20 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 21 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 22 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 23 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 24 | 25 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 26 | 27 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 28 | assertFloatEqual(t, 0, root.LayoutGetTop()) 29 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 30 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 31 | 32 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 33 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 34 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 35 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 36 | } 37 | 38 | func TestWrap_grandchild(t *testing.T) { 39 | config := NewConfig() 40 | 41 | root := NewNodeWithConfig(config) 42 | 43 | rootChild0 := NewNodeWithConfig(config) 44 | root.InsertChild(rootChild0, 0) 45 | 46 | rootChild0Child0 := NewNodeWithConfig(config) 47 | rootChild0Child0.StyleSetWidth(100) 48 | rootChild0Child0.StyleSetHeight(100) 49 | rootChild0.InsertChild(rootChild0Child0, 0) 50 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 51 | 52 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 53 | assertFloatEqual(t, 0, root.LayoutGetTop()) 54 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 55 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 56 | 57 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 58 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 59 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 60 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 61 | 62 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetLeft()) 63 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetTop()) 64 | assertFloatEqual(t, 100, rootChild0Child0.LayoutGetWidth()) 65 | assertFloatEqual(t, 100, rootChild0Child0.LayoutGetHeight()) 66 | 67 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 68 | 69 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 70 | assertFloatEqual(t, 0, root.LayoutGetTop()) 71 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 72 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 73 | 74 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 75 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 76 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 77 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 78 | 79 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetLeft()) 80 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetTop()) 81 | assertFloatEqual(t, 100, rootChild0Child0.LayoutGetWidth()) 82 | assertFloatEqual(t, 100, rootChild0Child0.LayoutGetHeight()) 83 | } 84 | -------------------------------------------------------------------------------- /flex/dirty_marking_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestDirty_propagation(t *testing.T) { 10 | root := NewNode() 11 | root.StyleSetAlignItems(AlignFlexStart) 12 | root.StyleSetWidth(100) 13 | root.StyleSetHeight(100) 14 | 15 | rootChild0 := NewNode() 16 | rootChild0.StyleSetWidth(50) 17 | rootChild0.StyleSetHeight(20) 18 | root.InsertChild(rootChild0, 0) 19 | 20 | rootChild1 := NewNode() 21 | rootChild1.StyleSetWidth(50) 22 | rootChild1.StyleSetHeight(20) 23 | root.InsertChild(rootChild1, 1) 24 | 25 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 26 | 27 | rootChild0.StyleSetWidth(20) 28 | 29 | assert.True(t, rootChild0.IsDirty) 30 | assert.False(t, rootChild1.IsDirty) 31 | assert.True(t, root.IsDirty) 32 | 33 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 34 | 35 | assert.False(t, rootChild0.IsDirty) 36 | assert.False(t, rootChild1.IsDirty) 37 | assert.False(t, root.IsDirty) 38 | 39 | } 40 | 41 | func TestDirty_propagation_only_if_prop_changed(t *testing.T) { 42 | root := NewNode() 43 | root.StyleSetAlignItems(AlignFlexStart) 44 | root.StyleSetWidth(100) 45 | root.StyleSetHeight(100) 46 | 47 | rootChild0 := NewNode() 48 | rootChild0.StyleSetWidth(50) 49 | rootChild0.StyleSetHeight(20) 50 | root.InsertChild(rootChild0, 0) 51 | 52 | rootChild1 := NewNode() 53 | rootChild1.StyleSetWidth(50) 54 | rootChild1.StyleSetHeight(20) 55 | root.InsertChild(rootChild1, 1) 56 | 57 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 58 | 59 | rootChild0.StyleSetWidth(50) 60 | 61 | assert.False(t, rootChild0.IsDirty) 62 | assert.False(t, rootChild1.IsDirty) 63 | assert.False(t, root.IsDirty) 64 | 65 | } 66 | 67 | func TestDirty_mark_all_children_as_dirty_when_display_changes(t *testing.T) { 68 | root := NewNode() 69 | root.StyleSetFlexDirection(FlexDirectionRow) 70 | root.StyleSetHeight(100) 71 | 72 | child0 := NewNode() 73 | child0.StyleSetFlexGrow(1) 74 | child1 := NewNode() 75 | child1.StyleSetFlexGrow(1) 76 | 77 | child1Child0 := NewNode() 78 | child1Child0Child0 := NewNode() 79 | child1Child0Child0.StyleSetWidth(8) 80 | child1Child0Child0.StyleSetHeight(16) 81 | 82 | child1Child0.InsertChild(child1Child0Child0, 0) 83 | 84 | child1.InsertChild(child1Child0, 0) 85 | root.InsertChild(child0, 0) 86 | root.InsertChild(child1, 0) 87 | 88 | child0.StyleSetDisplay(DisplayFlex) 89 | child1.StyleSetDisplay(DisplayNone) 90 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 91 | assertFloatEqual(t, 0, child1Child0Child0.LayoutGetWidth()) 92 | assertFloatEqual(t, 0, child1Child0Child0.LayoutGetHeight()) 93 | 94 | child0.StyleSetDisplay(DisplayNone) 95 | child1.StyleSetDisplay(DisplayFlex) 96 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 97 | assertFloatEqual(t, 8, child1Child0Child0.LayoutGetWidth()) 98 | assertFloatEqual(t, 16, child1Child0Child0.LayoutGetHeight()) 99 | 100 | child0.StyleSetDisplay(DisplayFlex) 101 | child1.StyleSetDisplay(DisplayNone) 102 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 103 | assertFloatEqual(t, 0, child1Child0Child0.LayoutGetWidth()) 104 | assertFloatEqual(t, 0, child1Child0Child0.LayoutGetHeight()) 105 | 106 | child0.StyleSetDisplay(DisplayNone) 107 | child1.StyleSetDisplay(DisplayFlex) 108 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 109 | assertFloatEqual(t, 8, child1Child0Child0.LayoutGetWidth()) 110 | assertFloatEqual(t, 16, child1Child0Child0.LayoutGetHeight()) 111 | } 112 | 113 | func TestDirty_node_only_if_children_are_actually_removed(t *testing.T) { 114 | root := NewNode() 115 | root.StyleSetAlignItems(AlignFlexStart) 116 | root.StyleSetWidth(50) 117 | root.StyleSetHeight(50) 118 | 119 | child0 := NewNode() 120 | child0.StyleSetWidth(50) 121 | child0.StyleSetHeight(25) 122 | root.InsertChild(child0, 0) 123 | 124 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 125 | 126 | child1 := NewNode() 127 | root.RemoveChild(child1) 128 | assert.False(t, root.IsDirty) 129 | 130 | root.RemoveChild(child0) 131 | assert.True(t, root.IsDirty) 132 | } 133 | -------------------------------------------------------------------------------- /flex/display_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestDisplay_none(t *testing.T) { 6 | config := NewConfig() 7 | 8 | root := NewNodeWithConfig(config) 9 | root.StyleSetFlexDirection(FlexDirectionRow) 10 | root.StyleSetWidth(100) 11 | root.StyleSetHeight(100) 12 | 13 | rootChild0 := NewNodeWithConfig(config) 14 | rootChild0.StyleSetFlexGrow(1) 15 | root.InsertChild(rootChild0, 0) 16 | 17 | rootChild1 := NewNodeWithConfig(config) 18 | rootChild1.StyleSetFlexGrow(1) 19 | rootChild1.StyleSetDisplay(DisplayNone) 20 | root.InsertChild(rootChild1, 1) 21 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 22 | 23 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 24 | assertFloatEqual(t, 0, root.LayoutGetTop()) 25 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 26 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 27 | 28 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 29 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 30 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 31 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 32 | 33 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 34 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 35 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 36 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 37 | 38 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 39 | 40 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 41 | assertFloatEqual(t, 0, root.LayoutGetTop()) 42 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 43 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 44 | 45 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 46 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 47 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 48 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 49 | 50 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 51 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 52 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 53 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 54 | } 55 | 56 | func TestDisplay_none_fixed_size(t *testing.T) { 57 | config := NewConfig() 58 | 59 | root := NewNodeWithConfig(config) 60 | root.StyleSetFlexDirection(FlexDirectionRow) 61 | root.StyleSetWidth(100) 62 | root.StyleSetHeight(100) 63 | 64 | rootChild0 := NewNodeWithConfig(config) 65 | rootChild0.StyleSetFlexGrow(1) 66 | root.InsertChild(rootChild0, 0) 67 | 68 | rootChild1 := NewNodeWithConfig(config) 69 | rootChild1.StyleSetWidth(20) 70 | rootChild1.StyleSetHeight(20) 71 | rootChild1.StyleSetDisplay(DisplayNone) 72 | root.InsertChild(rootChild1, 1) 73 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 74 | 75 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 76 | assertFloatEqual(t, 0, root.LayoutGetTop()) 77 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 78 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 79 | 80 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 81 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 82 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 83 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 84 | 85 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 86 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 87 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 88 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 89 | 90 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 91 | 92 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 93 | assertFloatEqual(t, 0, root.LayoutGetTop()) 94 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 95 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 96 | 97 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 98 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 99 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 100 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 101 | 102 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 103 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 104 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 105 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 106 | } 107 | 108 | func TestDisplay_none_with_margin(t *testing.T) { 109 | config := NewConfig() 110 | 111 | root := NewNodeWithConfig(config) 112 | root.StyleSetFlexDirection(FlexDirectionRow) 113 | root.StyleSetWidth(100) 114 | root.StyleSetHeight(100) 115 | 116 | rootChild0 := NewNodeWithConfig(config) 117 | rootChild0.StyleSetMargin(EdgeLeft, 10) 118 | rootChild0.StyleSetMargin(EdgeTop, 10) 119 | rootChild0.StyleSetMargin(EdgeRight, 10) 120 | rootChild0.StyleSetMargin(EdgeBottom, 10) 121 | rootChild0.StyleSetWidth(20) 122 | rootChild0.StyleSetHeight(20) 123 | rootChild0.StyleSetDisplay(DisplayNone) 124 | root.InsertChild(rootChild0, 0) 125 | 126 | rootChild1 := NewNodeWithConfig(config) 127 | rootChild1.StyleSetFlexGrow(1) 128 | root.InsertChild(rootChild1, 1) 129 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 130 | 131 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 132 | assertFloatEqual(t, 0, root.LayoutGetTop()) 133 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 134 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 135 | 136 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 137 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 138 | assertFloatEqual(t, 0, rootChild0.LayoutGetWidth()) 139 | assertFloatEqual(t, 0, rootChild0.LayoutGetHeight()) 140 | 141 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 142 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 143 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 144 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 145 | 146 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 147 | 148 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 149 | assertFloatEqual(t, 0, root.LayoutGetTop()) 150 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 151 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 152 | 153 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 154 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 155 | assertFloatEqual(t, 0, rootChild0.LayoutGetWidth()) 156 | assertFloatEqual(t, 0, rootChild0.LayoutGetHeight()) 157 | 158 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 159 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 160 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 161 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 162 | } 163 | 164 | func TestDisplay_none_with_child(t *testing.T) { 165 | config := NewConfig() 166 | 167 | root := NewNodeWithConfig(config) 168 | root.StyleSetFlexDirection(FlexDirectionRow) 169 | root.StyleSetWidth(100) 170 | root.StyleSetHeight(100) 171 | 172 | rootChild0 := NewNodeWithConfig(config) 173 | rootChild0.StyleSetFlexGrow(1) 174 | rootChild0.StyleSetFlexShrink(1) 175 | rootChild0.StyleSetFlexBasisPercent(0) 176 | root.InsertChild(rootChild0, 0) 177 | 178 | rootChild1 := NewNodeWithConfig(config) 179 | rootChild1.StyleSetFlexGrow(1) 180 | rootChild1.StyleSetFlexShrink(1) 181 | rootChild1.StyleSetFlexBasisPercent(0) 182 | rootChild1.StyleSetDisplay(DisplayNone) 183 | root.InsertChild(rootChild1, 1) 184 | 185 | rootChild1child0 := NewNodeWithConfig(config) 186 | rootChild1child0.StyleSetFlexGrow(1) 187 | rootChild1child0.StyleSetFlexShrink(1) 188 | rootChild1child0.StyleSetFlexBasisPercent(0) 189 | rootChild1child0.StyleSetWidth(20) 190 | rootChild1child0.StyleSetMinWidth(0) 191 | rootChild1child0.StyleSetMinHeight(0) 192 | rootChild1.InsertChild(rootChild1child0, 0) 193 | 194 | rootChild2 := NewNodeWithConfig(config) 195 | rootChild2.StyleSetFlexGrow(1) 196 | rootChild2.StyleSetFlexShrink(1) 197 | rootChild2.StyleSetFlexBasisPercent(0) 198 | root.InsertChild(rootChild2, 2) 199 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 200 | 201 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 202 | assertFloatEqual(t, 0, root.LayoutGetTop()) 203 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 204 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 205 | 206 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 207 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 208 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 209 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 210 | 211 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 212 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 213 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 214 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 215 | 216 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetLeft()) 217 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetTop()) 218 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetWidth()) 219 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetHeight()) 220 | 221 | assertFloatEqual(t, 50, rootChild2.LayoutGetLeft()) 222 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 223 | assertFloatEqual(t, 50, rootChild2.LayoutGetWidth()) 224 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 225 | 226 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 227 | 228 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 229 | assertFloatEqual(t, 0, root.LayoutGetTop()) 230 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 231 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 232 | 233 | assertFloatEqual(t, 50, rootChild0.LayoutGetLeft()) 234 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 235 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 236 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 237 | 238 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 239 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 240 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 241 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 242 | 243 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetLeft()) 244 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetTop()) 245 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetWidth()) 246 | assertFloatEqual(t, 0, rootChild1child0.LayoutGetHeight()) 247 | 248 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 249 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 250 | assertFloatEqual(t, 50, rootChild2.LayoutGetWidth()) 251 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 252 | } 253 | 254 | func TestDisplay_none_with_position(t *testing.T) { 255 | config := NewConfig() 256 | 257 | root := NewNodeWithConfig(config) 258 | root.StyleSetFlexDirection(FlexDirectionRow) 259 | root.StyleSetWidth(100) 260 | root.StyleSetHeight(100) 261 | 262 | rootChild0 := NewNodeWithConfig(config) 263 | rootChild0.StyleSetFlexGrow(1) 264 | root.InsertChild(rootChild0, 0) 265 | 266 | rootChild1 := NewNodeWithConfig(config) 267 | rootChild1.StyleSetFlexGrow(1) 268 | rootChild1.StyleSetPosition(EdgeTop, 10) 269 | rootChild1.StyleSetDisplay(DisplayNone) 270 | root.InsertChild(rootChild1, 1) 271 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 272 | 273 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 274 | assertFloatEqual(t, 0, root.LayoutGetTop()) 275 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 276 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 277 | 278 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 279 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 280 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 281 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 282 | 283 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 284 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 285 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 286 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 287 | 288 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 289 | 290 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 291 | assertFloatEqual(t, 0, root.LayoutGetTop()) 292 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 293 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 294 | 295 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 296 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 297 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 298 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 299 | 300 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 301 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 302 | assertFloatEqual(t, 0, rootChild1.LayoutGetWidth()) 303 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 304 | } 305 | -------------------------------------------------------------------------------- /flex/edge_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestStart_overrides(t *testing.T) { 6 | root := NewNode() 7 | root.StyleSetFlexDirection(FlexDirectionRow) 8 | root.StyleSetWidth(100) 9 | root.StyleSetHeight(100) 10 | 11 | rootChild0 := NewNode() 12 | rootChild0.StyleSetFlexGrow(1) 13 | rootChild0.StyleSetMargin(EdgeStart, 10) 14 | rootChild0.StyleSetMargin(EdgeLeft, 20) 15 | rootChild0.StyleSetMargin(EdgeRight, 20) 16 | root.InsertChild(rootChild0, 0) 17 | 18 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 19 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 20 | assertFloatEqual(t, 20, rootChild0.LayoutGetRight()) 21 | 22 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 23 | assertFloatEqual(t, 20, rootChild0.LayoutGetLeft()) 24 | assertFloatEqual(t, 10, rootChild0.LayoutGetRight()) 25 | } 26 | 27 | func TestEnd_overrides(t *testing.T) { 28 | root := NewNode() 29 | root.StyleSetFlexDirection(FlexDirectionRow) 30 | root.StyleSetWidth(100) 31 | root.StyleSetHeight(100) 32 | 33 | rootChild0 := NewNode() 34 | rootChild0.StyleSetFlexGrow(1) 35 | rootChild0.StyleSetMargin(EdgeEnd, 10) 36 | rootChild0.StyleSetMargin(EdgeLeft, 20) 37 | rootChild0.StyleSetMargin(EdgeRight, 20) 38 | root.InsertChild(rootChild0, 0) 39 | 40 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 41 | assertFloatEqual(t, 20, rootChild0.LayoutGetLeft()) 42 | assertFloatEqual(t, 10, rootChild0.LayoutGetRight()) 43 | 44 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 45 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 46 | assertFloatEqual(t, 20, rootChild0.LayoutGetRight()) 47 | } 48 | 49 | func TestHorizontal_overridden(t *testing.T) { 50 | root := NewNode() 51 | root.StyleSetFlexDirection(FlexDirectionRow) 52 | root.StyleSetWidth(100) 53 | root.StyleSetHeight(100) 54 | 55 | rootChild0 := NewNode() 56 | rootChild0.StyleSetFlexGrow(1) 57 | rootChild0.StyleSetMargin(EdgeHorizontal, 10) 58 | rootChild0.StyleSetMargin(EdgeLeft, 20) 59 | root.InsertChild(rootChild0, 0) 60 | 61 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 62 | assertFloatEqual(t, 20, rootChild0.LayoutGetLeft()) 63 | assertFloatEqual(t, 10, rootChild0.LayoutGetRight()) 64 | } 65 | 66 | func TestVertical_overridden(t *testing.T) { 67 | root := NewNode() 68 | root.StyleSetFlexDirection(FlexDirectionColumn) 69 | root.StyleSetWidth(100) 70 | root.StyleSetHeight(100) 71 | 72 | rootChild0 := NewNode() 73 | rootChild0.StyleSetFlexGrow(1) 74 | rootChild0.StyleSetMargin(EdgeVertical, 10) 75 | rootChild0.StyleSetMargin(EdgeTop, 20) 76 | root.InsertChild(rootChild0, 0) 77 | 78 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 79 | assertFloatEqual(t, 20, rootChild0.LayoutGetTop()) 80 | assertFloatEqual(t, 10, rootChild0.LayoutGetBottom()) 81 | } 82 | 83 | func TestHorizontal_overrides_all(t *testing.T) { 84 | root := NewNode() 85 | root.StyleSetFlexDirection(FlexDirectionColumn) 86 | root.StyleSetWidth(100) 87 | root.StyleSetHeight(100) 88 | 89 | rootChild0 := NewNode() 90 | rootChild0.StyleSetFlexGrow(1) 91 | rootChild0.StyleSetMargin(EdgeHorizontal, 10) 92 | rootChild0.StyleSetMargin(EdgeAll, 20) 93 | root.InsertChild(rootChild0, 0) 94 | 95 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 96 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 97 | assertFloatEqual(t, 20, rootChild0.LayoutGetTop()) 98 | assertFloatEqual(t, 10, rootChild0.LayoutGetRight()) 99 | assertFloatEqual(t, 20, rootChild0.LayoutGetBottom()) 100 | } 101 | 102 | func TestVertical_overrides_all(t *testing.T) { 103 | root := NewNode() 104 | root.StyleSetFlexDirection(FlexDirectionColumn) 105 | root.StyleSetWidth(100) 106 | root.StyleSetHeight(100) 107 | 108 | rootChild0 := NewNode() 109 | rootChild0.StyleSetFlexGrow(1) 110 | rootChild0.StyleSetMargin(EdgeVertical, 10) 111 | rootChild0.StyleSetMargin(EdgeAll, 20) 112 | root.InsertChild(rootChild0, 0) 113 | 114 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 115 | assertFloatEqual(t, 20, rootChild0.LayoutGetLeft()) 116 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 117 | assertFloatEqual(t, 20, rootChild0.LayoutGetRight()) 118 | assertFloatEqual(t, 10, rootChild0.LayoutGetBottom()) 119 | } 120 | 121 | func TestAll_overridden(t *testing.T) { 122 | root := NewNode() 123 | root.StyleSetFlexDirection(FlexDirectionColumn) 124 | root.StyleSetWidth(100) 125 | root.StyleSetHeight(100) 126 | 127 | rootChild0 := NewNode() 128 | rootChild0.StyleSetFlexGrow(1) 129 | rootChild0.StyleSetMargin(EdgeLeft, 10) 130 | rootChild0.StyleSetMargin(EdgeTop, 10) 131 | rootChild0.StyleSetMargin(EdgeRight, 10) 132 | rootChild0.StyleSetMargin(EdgeBottom, 10) 133 | rootChild0.StyleSetMargin(EdgeAll, 20) 134 | root.InsertChild(rootChild0, 0) 135 | 136 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 137 | assertFloatEqual(t, 10, rootChild0.LayoutGetLeft()) 138 | assertFloatEqual(t, 10, rootChild0.LayoutGetTop()) 139 | assertFloatEqual(t, 10, rootChild0.LayoutGetRight()) 140 | assertFloatEqual(t, 10, rootChild0.LayoutGetBottom()) 141 | } 142 | -------------------------------------------------------------------------------- /flex/enums.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | // port of YGEnums.h 4 | 5 | // Align describes align flex attribute 6 | type Align int 7 | 8 | const ( 9 | // AlignAuto is "auto" 10 | AlignAuto Align = iota 11 | // AlignFlexStart is "flex-start" 12 | AlignFlexStart 13 | // AlignCenter if "center" 14 | AlignCenter 15 | // AlignFlexEnd is "flex-end" 16 | AlignFlexEnd 17 | // AlignStretch is "strech" 18 | AlignStretch 19 | // AlignBaseline is "baseline" 20 | AlignBaseline 21 | // AlignSpaceBetween is "space-between" 22 | AlignSpaceBetween 23 | // AlignSpaceAround is "space-around" 24 | AlignSpaceAround 25 | ) 26 | 27 | // Dimension represents dimention 28 | type Dimension int 29 | 30 | const ( 31 | // DimensionWidth is width 32 | DimensionWidth Dimension = iota 33 | // DimensionHeight is height 34 | DimensionHeight 35 | ) 36 | 37 | // Direction represents right-to-left or left-to-right direction 38 | type Direction int 39 | 40 | const ( 41 | // DirectionInherit is "inherit" 42 | DirectionInherit Direction = iota 43 | // DirectionLTR is "ltr" 44 | DirectionLTR 45 | // DirectionRTL is "rtl" 46 | DirectionRTL 47 | ) 48 | 49 | // Display is "display" property 50 | type Display int 51 | 52 | const ( 53 | // DisplayFlex is "flex" 54 | DisplayFlex Display = iota 55 | // DisplayNone is "none" 56 | DisplayNone 57 | ) 58 | 59 | // Edge represents an edge 60 | type Edge int 61 | 62 | const ( 63 | // EdgeLeft is left edge 64 | EdgeLeft Edge = iota 65 | // EdgeTop is top edge 66 | EdgeTop 67 | // EdgeRight is right edge 68 | EdgeRight 69 | // EdgeBottom is bottom edge 70 | EdgeBottom 71 | // EdgeStart is start edge 72 | EdgeStart 73 | // EdgeEnd is end edge 74 | EdgeEnd 75 | // EdgeHorizontal is horizontal edge 76 | EdgeHorizontal 77 | // EdgeVertical is vertical edge 78 | EdgeVertical 79 | // EdgeAll is all edge 80 | EdgeAll 81 | ) 82 | 83 | const ( 84 | // EdgeCount is count of edges 85 | EdgeCount = 9 86 | ) 87 | 88 | // ExperimentalFeature defines experimental features 89 | type ExperimentalFeature int 90 | 91 | const ( 92 | // ExperimentalFeatureWebFlexBasis is web flex basis 93 | ExperimentalFeatureWebFlexBasis ExperimentalFeature = iota 94 | ) 95 | 96 | const ( 97 | experimentalFeatureCount = 1 98 | ) 99 | 100 | // FlexDirection describes "flex-direction" property 101 | type FlexDirection int 102 | 103 | const ( 104 | // FlexDirectionColumn is "column" 105 | FlexDirectionColumn FlexDirection = iota 106 | // FlexDirectionColumnReverse is "column-reverse" 107 | FlexDirectionColumnReverse 108 | // FlexDirectionRow is "row" 109 | FlexDirectionRow 110 | // FlexDirectionRowReverse is "row-reverse" 111 | FlexDirectionRowReverse 112 | ) 113 | 114 | // Justify is "justify" property 115 | type Justify int 116 | 117 | const ( 118 | // JustifyFlexStart is "flex-start" 119 | JustifyFlexStart Justify = iota 120 | // JustifyCenter is "center" 121 | JustifyCenter 122 | // JustifyFlexEnd is "flex-end" 123 | JustifyFlexEnd 124 | // JustifySpaceBetween is "space-between" 125 | JustifySpaceBetween 126 | // JustifySpaceAround is "space-around" 127 | JustifySpaceAround 128 | ) 129 | 130 | // LogLevel represents log level 131 | type LogLevel int 132 | 133 | const ( 134 | LogLevelError LogLevel = iota 135 | LogLevelWarn 136 | LogLevelInfo 137 | LogLevelDebug 138 | LogLevelVerbose 139 | LogLevelFatal 140 | ) 141 | 142 | // MeasureMode defines measurement mode 143 | type MeasureMode int 144 | 145 | const ( 146 | // MeasureModeUndefined is undefined 147 | MeasureModeUndefined MeasureMode = iota 148 | // MeasureModeExactly is exactly 149 | MeasureModeExactly 150 | // MeasureModeAtMost is at-most 151 | MeasureModeAtMost 152 | ) 153 | 154 | const ( 155 | measureModeCount = 3 156 | ) 157 | 158 | // NodeType defines node type 159 | type NodeType int 160 | 161 | const ( 162 | // NodeTypeDefault is default node 163 | NodeTypeDefault NodeType = iota 164 | // NodeTypeText is text node 165 | NodeTypeText 166 | ) 167 | 168 | // Overflow describes "overflow" property 169 | type Overflow int 170 | 171 | const ( 172 | // OverflowVisible is "visible" 173 | OverflowVisible Overflow = iota 174 | // OverflowHidden is "hidden" 175 | OverflowHidden 176 | // OverflowScroll is "scroll" 177 | OverflowScroll 178 | ) 179 | 180 | // PositionType is "position" property 181 | type PositionType int 182 | 183 | const ( 184 | // PositionTypeRelative is "relative" 185 | PositionTypeRelative PositionType = iota 186 | // PositionTypeAbsolute is "absolute" 187 | PositionTypeAbsolute 188 | ) 189 | 190 | type PrintOptions int 191 | 192 | const ( 193 | PrintOptionsLayout PrintOptions = 1 << iota 194 | PrintOptionsStyle 195 | PrintOptionsChildren 196 | ) 197 | 198 | // Unit is "unit" property 199 | type Unit int 200 | 201 | const ( 202 | // UnitUndefined is "undefined" 203 | UnitUndefined Unit = iota 204 | // UnitPoint is "point" 205 | UnitPoint 206 | // UnitPercent is "percent" 207 | UnitPercent 208 | // UnitAuto is "auto" 209 | UnitAuto 210 | ) 211 | 212 | // Wrap is "wrap" property 213 | type Wrap int 214 | 215 | const ( 216 | // WrapNoWrap is "no-wrap" 217 | WrapNoWrap Wrap = iota 218 | // WrapWrap is "wrap" 219 | WrapWrap 220 | // WrapWrapReverse is "reverse" 221 | WrapWrapReverse 222 | ) 223 | 224 | // AlignToString returns string version of Align enum 225 | func AlignToString(value Align) string { 226 | switch value { 227 | case AlignAuto: 228 | return "auto" 229 | case AlignFlexStart: 230 | return "flex-start" 231 | case AlignCenter: 232 | return "center" 233 | case AlignFlexEnd: 234 | return "flex-end" 235 | case AlignStretch: 236 | return "stretch" 237 | case AlignBaseline: 238 | return "baseline" 239 | case AlignSpaceBetween: 240 | return "space-between" 241 | case AlignSpaceAround: 242 | return "space-around" 243 | } 244 | return "unknown" 245 | } 246 | 247 | // DimensionToString returns string version of Dimension enum 248 | func DimensionToString(value Dimension) string { 249 | switch value { 250 | case DimensionWidth: 251 | return "width" 252 | case DimensionHeight: 253 | return "height" 254 | } 255 | return "unknown" 256 | } 257 | 258 | // DirectionToString returns string version of Direction enum 259 | func DirectionToString(value Direction) string { 260 | switch value { 261 | case DirectionInherit: 262 | return "inherit" 263 | case DirectionLTR: 264 | return "ltr" 265 | case DirectionRTL: 266 | return "rtl" 267 | } 268 | return "unknown" 269 | } 270 | 271 | // DisplayToString returns string version of Display enum 272 | func DisplayToString(value Display) string { 273 | switch value { 274 | case DisplayFlex: 275 | return "flex" 276 | case DisplayNone: 277 | return "none" 278 | } 279 | return "unknown" 280 | } 281 | 282 | // EdgeToString returns string version of Edge enum 283 | func EdgeToString(value Edge) string { 284 | switch value { 285 | case EdgeLeft: 286 | return "left" 287 | case EdgeTop: 288 | return "top" 289 | case EdgeRight: 290 | return "right" 291 | case EdgeBottom: 292 | return "bottom" 293 | case EdgeStart: 294 | return "start" 295 | case EdgeEnd: 296 | return "end" 297 | case EdgeHorizontal: 298 | return "horizontal" 299 | case EdgeVertical: 300 | return "vertical" 301 | case EdgeAll: 302 | return "all" 303 | } 304 | return "unknown" 305 | } 306 | 307 | // ExperimentalFeatureToString returns string version of ExperimentalFeature enum 308 | func ExperimentalFeatureToString(value ExperimentalFeature) string { 309 | switch value { 310 | case ExperimentalFeatureWebFlexBasis: 311 | return "web-flex-basis" 312 | } 313 | return "unknown" 314 | } 315 | 316 | // FlexDirectionToString returns string version of FlexDirection enum 317 | func FlexDirectionToString(value FlexDirection) string { 318 | switch value { 319 | case FlexDirectionColumn: 320 | return "column" 321 | case FlexDirectionColumnReverse: 322 | return "column-reverse" 323 | case FlexDirectionRow: 324 | return "row" 325 | case FlexDirectionRowReverse: 326 | return "row-reverse" 327 | } 328 | return "unknown" 329 | } 330 | 331 | // JustifyToString returns string version of Justify enum 332 | func JustifyToString(value Justify) string { 333 | switch value { 334 | case JustifyFlexStart: 335 | return "flex-start" 336 | case JustifyCenter: 337 | return "center" 338 | case JustifyFlexEnd: 339 | return "flex-end" 340 | case JustifySpaceBetween: 341 | return "space-between" 342 | case JustifySpaceAround: 343 | return "space-around" 344 | } 345 | return "unknown" 346 | } 347 | 348 | // LogLevelToString returns string version of LogLevel enum 349 | func LogLevelToString(value LogLevel) string { 350 | switch value { 351 | case LogLevelError: 352 | return "error" 353 | case LogLevelWarn: 354 | return "warn" 355 | case LogLevelInfo: 356 | return "info" 357 | case LogLevelDebug: 358 | return "debug" 359 | case LogLevelVerbose: 360 | return "verbose" 361 | case LogLevelFatal: 362 | return "fatal" 363 | } 364 | return "unknown" 365 | } 366 | 367 | // MeasureModeToString returns string version of MeasureMode enum 368 | func MeasureModeToString(value MeasureMode) string { 369 | switch value { 370 | case MeasureModeUndefined: 371 | return "undefined" 372 | case MeasureModeExactly: 373 | return "exactly" 374 | case MeasureModeAtMost: 375 | return "at-most" 376 | } 377 | return "unknown" 378 | } 379 | 380 | // NodeTypeToString returns string version of NodeType enum 381 | func NodeTypeToString(value NodeType) string { 382 | switch value { 383 | case NodeTypeDefault: 384 | return "default" 385 | case NodeTypeText: 386 | return "text" 387 | } 388 | return "unknown" 389 | } 390 | 391 | // OverflowToString returns string version of Overflow enum 392 | func OverflowToString(value Overflow) string { 393 | switch value { 394 | case OverflowVisible: 395 | return "visible" 396 | case OverflowHidden: 397 | return "hidden" 398 | case OverflowScroll: 399 | return "scroll" 400 | } 401 | return "unknown" 402 | } 403 | 404 | // PositionTypeToString returns string version of PositionType enum 405 | func PositionTypeToString(value PositionType) string { 406 | switch value { 407 | case PositionTypeRelative: 408 | return "relative" 409 | case PositionTypeAbsolute: 410 | return "absolute" 411 | } 412 | return "unknown" 413 | } 414 | 415 | // PrintOptionsToString returns string version of PrintOptions enum 416 | func PrintOptionsToString(value PrintOptions) string { 417 | switch value { 418 | case PrintOptionsLayout: 419 | return "layout" 420 | case PrintOptionsStyle: 421 | return "style" 422 | case PrintOptionsChildren: 423 | return "children" 424 | } 425 | return "unknown" 426 | } 427 | 428 | // UnitToString returns string version of Unit enum 429 | func UnitToString(value Unit) string { 430 | switch value { 431 | case UnitUndefined: 432 | return "undefined" 433 | case UnitPoint: 434 | return "point" 435 | case UnitPercent: 436 | return "percent" 437 | case UnitAuto: 438 | return "auto" 439 | } 440 | return "unknown" 441 | } 442 | 443 | // WrapToString returns string version of Wrap enum 444 | func WrapToString(value Wrap) string { 445 | switch value { 446 | case WrapNoWrap: 447 | return "no-wrap" 448 | case WrapWrap: 449 | return "wrap" 450 | case WrapWrapReverse: 451 | return "wrap-reverse" 452 | } 453 | return "unknown" 454 | } 455 | -------------------------------------------------------------------------------- /flex/flex_direction_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestFlex_direction_column_no_height(t *testing.T) { 6 | config := NewConfig() 7 | root := NewNodeWithConfig(config) 8 | root.StyleSetWidth(100) 9 | 10 | rootChild0 := NewNodeWithConfig(config) 11 | rootChild0.StyleSetHeight(10) 12 | root.InsertChild(rootChild0, 0) 13 | 14 | rootChild1 := NewNodeWithConfig(config) 15 | rootChild1.StyleSetHeight(10) 16 | root.InsertChild(rootChild1, 1) 17 | 18 | rootChild2 := NewNodeWithConfig(config) 19 | rootChild2.StyleSetHeight(10) 20 | root.InsertChild(rootChild2, 2) 21 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 22 | 23 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 24 | assertFloatEqual(t, 0, root.LayoutGetTop()) 25 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 26 | assertFloatEqual(t, 30, root.LayoutGetHeight()) 27 | 28 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 29 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 30 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 31 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 32 | 33 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 34 | assertFloatEqual(t, 10, rootChild1.LayoutGetTop()) 35 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 36 | assertFloatEqual(t, 10, rootChild1.LayoutGetHeight()) 37 | 38 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 39 | assertFloatEqual(t, 20, rootChild2.LayoutGetTop()) 40 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 41 | assertFloatEqual(t, 10, rootChild2.LayoutGetHeight()) 42 | 43 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 44 | 45 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 46 | assertFloatEqual(t, 0, root.LayoutGetTop()) 47 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 48 | assertFloatEqual(t, 30, root.LayoutGetHeight()) 49 | 50 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 51 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 52 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 53 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 54 | 55 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 56 | assertFloatEqual(t, 10, rootChild1.LayoutGetTop()) 57 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 58 | assertFloatEqual(t, 10, rootChild1.LayoutGetHeight()) 59 | 60 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 61 | assertFloatEqual(t, 20, rootChild2.LayoutGetTop()) 62 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 63 | assertFloatEqual(t, 10, rootChild2.LayoutGetHeight()) 64 | } 65 | 66 | func TestFlex_direction_row_no_width(t *testing.T) { 67 | config := NewConfig() 68 | 69 | root := NewNodeWithConfig(config) 70 | root.StyleSetFlexDirection(FlexDirectionRow) 71 | root.StyleSetHeight(100) 72 | 73 | rootChild0 := NewNodeWithConfig(config) 74 | rootChild0.StyleSetWidth(10) 75 | root.InsertChild(rootChild0, 0) 76 | 77 | rootChild1 := NewNodeWithConfig(config) 78 | rootChild1.StyleSetWidth(10) 79 | root.InsertChild(rootChild1, 1) 80 | 81 | rootChild2 := NewNodeWithConfig(config) 82 | rootChild2.StyleSetWidth(10) 83 | root.InsertChild(rootChild2, 2) 84 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 85 | 86 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 87 | assertFloatEqual(t, 0, root.LayoutGetTop()) 88 | assertFloatEqual(t, 30, root.LayoutGetWidth()) 89 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 90 | 91 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 92 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 93 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 94 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 95 | 96 | assertFloatEqual(t, 10, rootChild1.LayoutGetLeft()) 97 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 98 | assertFloatEqual(t, 10, rootChild1.LayoutGetWidth()) 99 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 100 | 101 | assertFloatEqual(t, 20, rootChild2.LayoutGetLeft()) 102 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 103 | assertFloatEqual(t, 10, rootChild2.LayoutGetWidth()) 104 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 105 | 106 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 107 | 108 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 109 | assertFloatEqual(t, 0, root.LayoutGetTop()) 110 | assertFloatEqual(t, 30, root.LayoutGetWidth()) 111 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 112 | 113 | assertFloatEqual(t, 20, rootChild0.LayoutGetLeft()) 114 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 115 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 116 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 117 | 118 | assertFloatEqual(t, 10, rootChild1.LayoutGetLeft()) 119 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 120 | assertFloatEqual(t, 10, rootChild1.LayoutGetWidth()) 121 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 122 | 123 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 124 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 125 | assertFloatEqual(t, 10, rootChild2.LayoutGetWidth()) 126 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 127 | } 128 | 129 | func TestFlex_direction_column(t *testing.T) { 130 | config := NewConfig() 131 | 132 | root := NewNodeWithConfig(config) 133 | root.StyleSetWidth(100) 134 | root.StyleSetHeight(100) 135 | 136 | rootChild0 := NewNodeWithConfig(config) 137 | rootChild0.StyleSetHeight(10) 138 | root.InsertChild(rootChild0, 0) 139 | 140 | rootChild1 := NewNodeWithConfig(config) 141 | rootChild1.StyleSetHeight(10) 142 | root.InsertChild(rootChild1, 1) 143 | 144 | rootChild2 := NewNodeWithConfig(config) 145 | rootChild2.StyleSetHeight(10) 146 | root.InsertChild(rootChild2, 2) 147 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 148 | 149 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 150 | assertFloatEqual(t, 0, root.LayoutGetTop()) 151 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 152 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 153 | 154 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 155 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 156 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 157 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 158 | 159 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 160 | assertFloatEqual(t, 10, rootChild1.LayoutGetTop()) 161 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 162 | assertFloatEqual(t, 10, rootChild1.LayoutGetHeight()) 163 | 164 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 165 | assertFloatEqual(t, 20, rootChild2.LayoutGetTop()) 166 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 167 | assertFloatEqual(t, 10, rootChild2.LayoutGetHeight()) 168 | 169 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 170 | 171 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 172 | assertFloatEqual(t, 0, root.LayoutGetTop()) 173 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 174 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 175 | 176 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 177 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 178 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 179 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 180 | 181 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 182 | assertFloatEqual(t, 10, rootChild1.LayoutGetTop()) 183 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 184 | assertFloatEqual(t, 10, rootChild1.LayoutGetHeight()) 185 | 186 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 187 | assertFloatEqual(t, 20, rootChild2.LayoutGetTop()) 188 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 189 | assertFloatEqual(t, 10, rootChild2.LayoutGetHeight()) 190 | } 191 | 192 | func TestFlex_direction_row(t *testing.T) { 193 | config := NewConfig() 194 | 195 | root := NewNodeWithConfig(config) 196 | root.StyleSetFlexDirection(FlexDirectionRow) 197 | root.StyleSetWidth(100) 198 | root.StyleSetHeight(100) 199 | 200 | rootChild0 := NewNodeWithConfig(config) 201 | rootChild0.StyleSetWidth(10) 202 | root.InsertChild(rootChild0, 0) 203 | 204 | rootChild1 := NewNodeWithConfig(config) 205 | rootChild1.StyleSetWidth(10) 206 | root.InsertChild(rootChild1, 1) 207 | 208 | rootChild2 := NewNodeWithConfig(config) 209 | rootChild2.StyleSetWidth(10) 210 | root.InsertChild(rootChild2, 2) 211 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 212 | 213 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 214 | assertFloatEqual(t, 0, root.LayoutGetTop()) 215 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 216 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 217 | 218 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 219 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 220 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 221 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 222 | 223 | assertFloatEqual(t, 10, rootChild1.LayoutGetLeft()) 224 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 225 | assertFloatEqual(t, 10, rootChild1.LayoutGetWidth()) 226 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 227 | 228 | assertFloatEqual(t, 20, rootChild2.LayoutGetLeft()) 229 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 230 | assertFloatEqual(t, 10, rootChild2.LayoutGetWidth()) 231 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 232 | 233 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 234 | 235 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 236 | assertFloatEqual(t, 0, root.LayoutGetTop()) 237 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 238 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 239 | 240 | assertFloatEqual(t, 90, rootChild0.LayoutGetLeft()) 241 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 242 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 243 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 244 | 245 | assertFloatEqual(t, 80, rootChild1.LayoutGetLeft()) 246 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 247 | assertFloatEqual(t, 10, rootChild1.LayoutGetWidth()) 248 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 249 | 250 | assertFloatEqual(t, 70, rootChild2.LayoutGetLeft()) 251 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 252 | assertFloatEqual(t, 10, rootChild2.LayoutGetWidth()) 253 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 254 | } 255 | 256 | func TestFlex_direction_column_reverse(t *testing.T) { 257 | config := NewConfig() 258 | 259 | root := NewNodeWithConfig(config) 260 | root.StyleSetFlexDirection(FlexDirectionColumnReverse) 261 | root.StyleSetWidth(100) 262 | root.StyleSetHeight(100) 263 | 264 | rootChild0 := NewNodeWithConfig(config) 265 | rootChild0.StyleSetHeight(10) 266 | root.InsertChild(rootChild0, 0) 267 | 268 | rootChild1 := NewNodeWithConfig(config) 269 | rootChild1.StyleSetHeight(10) 270 | root.InsertChild(rootChild1, 1) 271 | 272 | rootChild2 := NewNodeWithConfig(config) 273 | rootChild2.StyleSetHeight(10) 274 | root.InsertChild(rootChild2, 2) 275 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 276 | 277 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 278 | assertFloatEqual(t, 0, root.LayoutGetTop()) 279 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 280 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 281 | 282 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 283 | assertFloatEqual(t, 90, rootChild0.LayoutGetTop()) 284 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 285 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 286 | 287 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 288 | assertFloatEqual(t, 80, rootChild1.LayoutGetTop()) 289 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 290 | assertFloatEqual(t, 10, rootChild1.LayoutGetHeight()) 291 | 292 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 293 | assertFloatEqual(t, 70, rootChild2.LayoutGetTop()) 294 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 295 | assertFloatEqual(t, 10, rootChild2.LayoutGetHeight()) 296 | 297 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 298 | 299 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 300 | assertFloatEqual(t, 0, root.LayoutGetTop()) 301 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 302 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 303 | 304 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 305 | assertFloatEqual(t, 90, rootChild0.LayoutGetTop()) 306 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 307 | assertFloatEqual(t, 10, rootChild0.LayoutGetHeight()) 308 | 309 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 310 | assertFloatEqual(t, 80, rootChild1.LayoutGetTop()) 311 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 312 | assertFloatEqual(t, 10, rootChild1.LayoutGetHeight()) 313 | 314 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 315 | assertFloatEqual(t, 70, rootChild2.LayoutGetTop()) 316 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 317 | assertFloatEqual(t, 10, rootChild2.LayoutGetHeight()) 318 | } 319 | 320 | func TestFlex_direction_row_reverse(t *testing.T) { 321 | config := NewConfig() 322 | 323 | root := NewNodeWithConfig(config) 324 | root.StyleSetFlexDirection(FlexDirectionRowReverse) 325 | root.StyleSetWidth(100) 326 | root.StyleSetHeight(100) 327 | 328 | rootChild0 := NewNodeWithConfig(config) 329 | rootChild0.StyleSetWidth(10) 330 | root.InsertChild(rootChild0, 0) 331 | 332 | rootChild1 := NewNodeWithConfig(config) 333 | rootChild1.StyleSetWidth(10) 334 | root.InsertChild(rootChild1, 1) 335 | 336 | rootChild2 := NewNodeWithConfig(config) 337 | rootChild2.StyleSetWidth(10) 338 | root.InsertChild(rootChild2, 2) 339 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 340 | 341 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 342 | assertFloatEqual(t, 0, root.LayoutGetTop()) 343 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 344 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 345 | 346 | assertFloatEqual(t, 90, rootChild0.LayoutGetLeft()) 347 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 348 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 349 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 350 | 351 | assertFloatEqual(t, 80, rootChild1.LayoutGetLeft()) 352 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 353 | assertFloatEqual(t, 10, rootChild1.LayoutGetWidth()) 354 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 355 | 356 | assertFloatEqual(t, 70, rootChild2.LayoutGetLeft()) 357 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 358 | assertFloatEqual(t, 10, rootChild2.LayoutGetWidth()) 359 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 360 | 361 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 362 | 363 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 364 | assertFloatEqual(t, 0, root.LayoutGetTop()) 365 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 366 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 367 | 368 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 369 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 370 | assertFloatEqual(t, 10, rootChild0.LayoutGetWidth()) 371 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 372 | 373 | assertFloatEqual(t, 10, rootChild1.LayoutGetLeft()) 374 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 375 | assertFloatEqual(t, 10, rootChild1.LayoutGetWidth()) 376 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 377 | 378 | assertFloatEqual(t, 20, rootChild2.LayoutGetLeft()) 379 | assertFloatEqual(t, 0, rootChild2.LayoutGetTop()) 380 | assertFloatEqual(t, 10, rootChild2.LayoutGetWidth()) 381 | assertFloatEqual(t, 100, rootChild2.LayoutGetHeight()) 382 | } 383 | -------------------------------------------------------------------------------- /flex/flex_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import "testing" 4 | 5 | func TestFlex_basis_flex_grow_column(t *testing.T) { 6 | config := NewConfig() 7 | 8 | root := NewNodeWithConfig(config) 9 | root.StyleSetWidth(100) 10 | root.StyleSetHeight(100) 11 | 12 | rootChild0 := NewNodeWithConfig(config) 13 | rootChild0.StyleSetFlexGrow(1) 14 | rootChild0.StyleSetFlexBasis(50) 15 | root.InsertChild(rootChild0, 0) 16 | 17 | rootChild1 := NewNodeWithConfig(config) 18 | rootChild1.StyleSetFlexGrow(1) 19 | root.InsertChild(rootChild1, 1) 20 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 21 | 22 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 23 | assertFloatEqual(t, 0, root.LayoutGetTop()) 24 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 25 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 26 | 27 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 28 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 29 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 30 | assertFloatEqual(t, 75, rootChild0.LayoutGetHeight()) 31 | 32 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 33 | assertFloatEqual(t, 75, rootChild1.LayoutGetTop()) 34 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 35 | assertFloatEqual(t, 25, rootChild1.LayoutGetHeight()) 36 | 37 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 38 | 39 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 40 | assertFloatEqual(t, 0, root.LayoutGetTop()) 41 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 42 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 43 | 44 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 45 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 46 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 47 | assertFloatEqual(t, 75, rootChild0.LayoutGetHeight()) 48 | 49 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 50 | assertFloatEqual(t, 75, rootChild1.LayoutGetTop()) 51 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 52 | assertFloatEqual(t, 25, rootChild1.LayoutGetHeight()) 53 | } 54 | 55 | func TestFlex_basis_flex_grow_row(t *testing.T) { 56 | config := NewConfig() 57 | 58 | root := NewNodeWithConfig(config) 59 | root.StyleSetFlexDirection(FlexDirectionRow) 60 | root.StyleSetWidth(100) 61 | root.StyleSetHeight(100) 62 | 63 | rootChild0 := NewNodeWithConfig(config) 64 | rootChild0.StyleSetFlexGrow(1) 65 | rootChild0.StyleSetFlexBasis(50) 66 | root.InsertChild(rootChild0, 0) 67 | 68 | rootChild1 := NewNodeWithConfig(config) 69 | rootChild1.StyleSetFlexGrow(1) 70 | root.InsertChild(rootChild1, 1) 71 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 72 | 73 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 74 | assertFloatEqual(t, 0, root.LayoutGetTop()) 75 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 76 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 77 | 78 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 79 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 80 | assertFloatEqual(t, 75, rootChild0.LayoutGetWidth()) 81 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 82 | 83 | assertFloatEqual(t, 75, rootChild1.LayoutGetLeft()) 84 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 85 | assertFloatEqual(t, 25, rootChild1.LayoutGetWidth()) 86 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 87 | 88 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 89 | 90 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 91 | assertFloatEqual(t, 0, root.LayoutGetTop()) 92 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 93 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 94 | 95 | assertFloatEqual(t, 25, rootChild0.LayoutGetLeft()) 96 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 97 | assertFloatEqual(t, 75, rootChild0.LayoutGetWidth()) 98 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 99 | 100 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 101 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 102 | assertFloatEqual(t, 25, rootChild1.LayoutGetWidth()) 103 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 104 | } 105 | 106 | func TestFlex_basis_flex_shrink_column(t *testing.T) { 107 | config := NewConfig() 108 | 109 | root := NewNodeWithConfig(config) 110 | root.StyleSetWidth(100) 111 | root.StyleSetHeight(100) 112 | 113 | rootChild0 := NewNodeWithConfig(config) 114 | rootChild0.StyleSetFlexShrink(1) 115 | rootChild0.StyleSetFlexBasis(100) 116 | root.InsertChild(rootChild0, 0) 117 | 118 | rootChild1 := NewNodeWithConfig(config) 119 | rootChild1.StyleSetFlexBasis(50) 120 | root.InsertChild(rootChild1, 1) 121 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 122 | 123 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 124 | assertFloatEqual(t, 0, root.LayoutGetTop()) 125 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 126 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 127 | 128 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 129 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 130 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 131 | assertFloatEqual(t, 50, rootChild0.LayoutGetHeight()) 132 | 133 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 134 | assertFloatEqual(t, 50, rootChild1.LayoutGetTop()) 135 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 136 | assertFloatEqual(t, 50, rootChild1.LayoutGetHeight()) 137 | 138 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 139 | 140 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 141 | assertFloatEqual(t, 0, root.LayoutGetTop()) 142 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 143 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 144 | 145 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 146 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 147 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 148 | assertFloatEqual(t, 50, rootChild0.LayoutGetHeight()) 149 | 150 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 151 | assertFloatEqual(t, 50, rootChild1.LayoutGetTop()) 152 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 153 | assertFloatEqual(t, 50, rootChild1.LayoutGetHeight()) 154 | } 155 | 156 | func TestFlex_basis_flex_shrink_row(t *testing.T) { 157 | config := NewConfig() 158 | 159 | root := NewNodeWithConfig(config) 160 | root.StyleSetFlexDirection(FlexDirectionRow) 161 | root.StyleSetWidth(100) 162 | root.StyleSetHeight(100) 163 | 164 | rootChild0 := NewNodeWithConfig(config) 165 | rootChild0.StyleSetFlexShrink(1) 166 | rootChild0.StyleSetFlexBasis(100) 167 | root.InsertChild(rootChild0, 0) 168 | 169 | rootChild1 := NewNodeWithConfig(config) 170 | rootChild1.StyleSetFlexBasis(50) 171 | root.InsertChild(rootChild1, 1) 172 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 173 | 174 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 175 | assertFloatEqual(t, 0, root.LayoutGetTop()) 176 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 177 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 178 | 179 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 180 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 181 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 182 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 183 | 184 | assertFloatEqual(t, 50, rootChild1.LayoutGetLeft()) 185 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 186 | assertFloatEqual(t, 50, rootChild1.LayoutGetWidth()) 187 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 188 | 189 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 190 | 191 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 192 | assertFloatEqual(t, 0, root.LayoutGetTop()) 193 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 194 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 195 | 196 | assertFloatEqual(t, 50, rootChild0.LayoutGetLeft()) 197 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 198 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 199 | assertFloatEqual(t, 100, rootChild0.LayoutGetHeight()) 200 | 201 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 202 | assertFloatEqual(t, 0, rootChild1.LayoutGetTop()) 203 | assertFloatEqual(t, 50, rootChild1.LayoutGetWidth()) 204 | assertFloatEqual(t, 100, rootChild1.LayoutGetHeight()) 205 | } 206 | 207 | func TestFlex_shrink_to_zero(t *testing.T) { 208 | config := NewConfig() 209 | 210 | root := NewNodeWithConfig(config) 211 | root.StyleSetHeight(75) 212 | 213 | rootChild0 := NewNodeWithConfig(config) 214 | rootChild0.StyleSetWidth(50) 215 | rootChild0.StyleSetHeight(50) 216 | root.InsertChild(rootChild0, 0) 217 | 218 | rootChild1 := NewNodeWithConfig(config) 219 | rootChild1.StyleSetFlexShrink(1) 220 | rootChild1.StyleSetWidth(50) 221 | rootChild1.StyleSetHeight(50) 222 | root.InsertChild(rootChild1, 1) 223 | 224 | rootChild2 := NewNodeWithConfig(config) 225 | rootChild2.StyleSetWidth(50) 226 | rootChild2.StyleSetHeight(50) 227 | root.InsertChild(rootChild2, 2) 228 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 229 | 230 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 231 | assertFloatEqual(t, 0, root.LayoutGetTop()) 232 | assertFloatEqual(t, 50, root.LayoutGetWidth()) 233 | assertFloatEqual(t, 75, root.LayoutGetHeight()) 234 | 235 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 236 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 237 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 238 | assertFloatEqual(t, 50, rootChild0.LayoutGetHeight()) 239 | 240 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 241 | assertFloatEqual(t, 50, rootChild1.LayoutGetTop()) 242 | assertFloatEqual(t, 50, rootChild1.LayoutGetWidth()) 243 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 244 | 245 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 246 | assertFloatEqual(t, 50, rootChild2.LayoutGetTop()) 247 | assertFloatEqual(t, 50, rootChild2.LayoutGetWidth()) 248 | assertFloatEqual(t, 50, rootChild2.LayoutGetHeight()) 249 | 250 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 251 | 252 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 253 | assertFloatEqual(t, 0, root.LayoutGetTop()) 254 | assertFloatEqual(t, 50, root.LayoutGetWidth()) 255 | assertFloatEqual(t, 75, root.LayoutGetHeight()) 256 | 257 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 258 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 259 | assertFloatEqual(t, 50, rootChild0.LayoutGetWidth()) 260 | assertFloatEqual(t, 50, rootChild0.LayoutGetHeight()) 261 | 262 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 263 | assertFloatEqual(t, 50, rootChild1.LayoutGetTop()) 264 | assertFloatEqual(t, 50, rootChild1.LayoutGetWidth()) 265 | assertFloatEqual(t, 0, rootChild1.LayoutGetHeight()) 266 | 267 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 268 | assertFloatEqual(t, 50, rootChild2.LayoutGetTop()) 269 | assertFloatEqual(t, 50, rootChild2.LayoutGetWidth()) 270 | assertFloatEqual(t, 50, rootChild2.LayoutGetHeight()) 271 | } 272 | 273 | func TestFlex_basis_overrides_main_size(t *testing.T) { 274 | config := NewConfig() 275 | 276 | root := NewNodeWithConfig(config) 277 | root.StyleSetWidth(100) 278 | root.StyleSetHeight(100) 279 | 280 | rootChild0 := NewNodeWithConfig(config) 281 | rootChild0.StyleSetFlexGrow(1) 282 | rootChild0.StyleSetFlexBasis(50) 283 | rootChild0.StyleSetHeight(20) 284 | root.InsertChild(rootChild0, 0) 285 | 286 | rootChild1 := NewNodeWithConfig(config) 287 | rootChild1.StyleSetFlexGrow(1) 288 | rootChild1.StyleSetHeight(10) 289 | root.InsertChild(rootChild1, 1) 290 | 291 | rootChild2 := NewNodeWithConfig(config) 292 | rootChild2.StyleSetFlexGrow(1) 293 | rootChild2.StyleSetHeight(10) 294 | root.InsertChild(rootChild2, 2) 295 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 296 | 297 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 298 | assertFloatEqual(t, 0, root.LayoutGetTop()) 299 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 300 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 301 | 302 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 303 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 304 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 305 | assertFloatEqual(t, 60, rootChild0.LayoutGetHeight()) 306 | 307 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 308 | assertFloatEqual(t, 60, rootChild1.LayoutGetTop()) 309 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 310 | assertFloatEqual(t, 20, rootChild1.LayoutGetHeight()) 311 | 312 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 313 | assertFloatEqual(t, 80, rootChild2.LayoutGetTop()) 314 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 315 | assertFloatEqual(t, 20, rootChild2.LayoutGetHeight()) 316 | 317 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 318 | 319 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 320 | assertFloatEqual(t, 0, root.LayoutGetTop()) 321 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 322 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 323 | 324 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 325 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 326 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 327 | assertFloatEqual(t, 60, rootChild0.LayoutGetHeight()) 328 | 329 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 330 | assertFloatEqual(t, 60, rootChild1.LayoutGetTop()) 331 | assertFloatEqual(t, 100, rootChild1.LayoutGetWidth()) 332 | assertFloatEqual(t, 20, rootChild1.LayoutGetHeight()) 333 | 334 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 335 | assertFloatEqual(t, 80, rootChild2.LayoutGetTop()) 336 | assertFloatEqual(t, 100, rootChild2.LayoutGetWidth()) 337 | assertFloatEqual(t, 20, rootChild2.LayoutGetHeight()) 338 | } 339 | 340 | func TestFlex_grow_shrink_at_most(t *testing.T) { 341 | config := NewConfig() 342 | 343 | root := NewNodeWithConfig(config) 344 | root.StyleSetWidth(100) 345 | root.StyleSetHeight(100) 346 | 347 | rootChild0 := NewNodeWithConfig(config) 348 | root.InsertChild(rootChild0, 0) 349 | 350 | rootChild0Child0 := NewNodeWithConfig(config) 351 | rootChild0Child0.StyleSetFlexGrow(1) 352 | rootChild0Child0.StyleSetFlexShrink(1) 353 | rootChild0.InsertChild(rootChild0Child0, 0) 354 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 355 | 356 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 357 | assertFloatEqual(t, 0, root.LayoutGetTop()) 358 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 359 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 360 | 361 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 362 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 363 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 364 | assertFloatEqual(t, 0, rootChild0.LayoutGetHeight()) 365 | 366 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetLeft()) 367 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetTop()) 368 | assertFloatEqual(t, 100, rootChild0Child0.LayoutGetWidth()) 369 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetHeight()) 370 | 371 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 372 | 373 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 374 | assertFloatEqual(t, 0, root.LayoutGetTop()) 375 | assertFloatEqual(t, 100, root.LayoutGetWidth()) 376 | assertFloatEqual(t, 100, root.LayoutGetHeight()) 377 | 378 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 379 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 380 | assertFloatEqual(t, 100, rootChild0.LayoutGetWidth()) 381 | assertFloatEqual(t, 0, rootChild0.LayoutGetHeight()) 382 | 383 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetLeft()) 384 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetTop()) 385 | assertFloatEqual(t, 100, rootChild0Child0.LayoutGetWidth()) 386 | assertFloatEqual(t, 0, rootChild0Child0.LayoutGetHeight()) 387 | } 388 | 389 | func TestFlex_grow_less_than_factor_one(t *testing.T) { 390 | config := NewConfig() 391 | 392 | root := NewNodeWithConfig(config) 393 | root.StyleSetWidth(200) 394 | root.StyleSetHeight(500) 395 | 396 | rootChild0 := NewNodeWithConfig(config) 397 | rootChild0.StyleSetFlexGrow(0.2) 398 | rootChild0.StyleSetFlexBasis(40) 399 | root.InsertChild(rootChild0, 0) 400 | 401 | rootChild1 := NewNodeWithConfig(config) 402 | rootChild1.StyleSetFlexGrow(0.2) 403 | root.InsertChild(rootChild1, 1) 404 | 405 | rootChild2 := NewNodeWithConfig(config) 406 | rootChild2.StyleSetFlexGrow(0.4) 407 | root.InsertChild(rootChild2, 2) 408 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 409 | 410 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 411 | assertFloatEqual(t, 0, root.LayoutGetTop()) 412 | assertFloatEqual(t, 200, root.LayoutGetWidth()) 413 | assertFloatEqual(t, 500, root.LayoutGetHeight()) 414 | 415 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 416 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 417 | assertFloatEqual(t, 200, rootChild0.LayoutGetWidth()) 418 | assertFloatEqual(t, 132, rootChild0.LayoutGetHeight()) 419 | 420 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 421 | assertFloatEqual(t, 132, rootChild1.LayoutGetTop()) 422 | assertFloatEqual(t, 200, rootChild1.LayoutGetWidth()) 423 | assertFloatEqual(t, 92, rootChild1.LayoutGetHeight()) 424 | 425 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 426 | assertFloatEqual(t, 224, rootChild2.LayoutGetTop()) 427 | assertFloatEqual(t, 200, rootChild2.LayoutGetWidth()) 428 | assertFloatEqual(t, 184, rootChild2.LayoutGetHeight()) 429 | 430 | CalculateLayout(root, Undefined, Undefined, DirectionRTL) 431 | 432 | assertFloatEqual(t, 0, root.LayoutGetLeft()) 433 | assertFloatEqual(t, 0, root.LayoutGetTop()) 434 | assertFloatEqual(t, 200, root.LayoutGetWidth()) 435 | assertFloatEqual(t, 500, root.LayoutGetHeight()) 436 | 437 | assertFloatEqual(t, 0, rootChild0.LayoutGetLeft()) 438 | assertFloatEqual(t, 0, rootChild0.LayoutGetTop()) 439 | assertFloatEqual(t, 200, rootChild0.LayoutGetWidth()) 440 | assertFloatEqual(t, 132, rootChild0.LayoutGetHeight()) 441 | 442 | assertFloatEqual(t, 0, rootChild1.LayoutGetLeft()) 443 | assertFloatEqual(t, 132, rootChild1.LayoutGetTop()) 444 | assertFloatEqual(t, 200, rootChild1.LayoutGetWidth()) 445 | assertFloatEqual(t, 92, rootChild1.LayoutGetHeight()) 446 | 447 | assertFloatEqual(t, 0, rootChild2.LayoutGetLeft()) 448 | assertFloatEqual(t, 224, rootChild2.LayoutGetTop()) 449 | assertFloatEqual(t, 200, rootChild2.LayoutGetWidth()) 450 | assertFloatEqual(t, 184, rootChild2.LayoutGetHeight()) 451 | } 452 | -------------------------------------------------------------------------------- /flex/had_overflow_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func newHadOverflowTests() (*Config, *Node) { 10 | config := NewConfig() 11 | root := NewNodeWithConfig(config) 12 | root.StyleSetWidth(200) 13 | root.StyleSetHeight(100) 14 | root.StyleSetFlexDirection(FlexDirectionColumn) 15 | root.StyleSetFlexWrap(WrapNoWrap) 16 | return config, root 17 | } 18 | 19 | func TestChildren_overflow_no_wrap_and_no_flex_children(t *testing.T) { 20 | config, root := newHadOverflowTests() 21 | child0 := NewNodeWithConfig(config) 22 | child0.StyleSetWidth(80) 23 | child0.StyleSetHeight(40) 24 | child0.StyleSetMargin(EdgeTop, 10) 25 | child0.StyleSetMargin(EdgeBottom, 15) 26 | root.InsertChild(child0, 0) 27 | child1 := NewNodeWithConfig(config) 28 | child1.StyleSetWidth(80) 29 | child1.StyleSetHeight(40) 30 | child1.StyleSetMargin(EdgeBottom, 5) 31 | root.InsertChild(child1, 1) 32 | 33 | CalculateLayout(root, 200, 100, DirectionLTR) 34 | 35 | assert.True(t, root.Layout.HadOverflow) 36 | } 37 | 38 | func TestSpacing_overflow_no_wrap_and_no_flex_children(t *testing.T) { 39 | config, root := newHadOverflowTests() 40 | child0 := NewNodeWithConfig(config) 41 | child0.StyleSetWidth(80) 42 | child0.StyleSetHeight(40) 43 | child0.StyleSetMargin(EdgeTop, 10) 44 | child0.StyleSetMargin(EdgeBottom, 10) 45 | root.InsertChild(child0, 0) 46 | child1 := NewNodeWithConfig(config) 47 | child1.StyleSetWidth(80) 48 | child1.StyleSetHeight(40) 49 | child1.StyleSetMargin(EdgeBottom, 5) 50 | root.InsertChild(child1, 1) 51 | 52 | CalculateLayout(root, 200, 100, DirectionLTR) 53 | 54 | assert.True(t, root.Layout.HadOverflow) 55 | } 56 | 57 | func TestNo_overflow_no_wrap_and_flex_children(t *testing.T) { 58 | config, root := newHadOverflowTests() 59 | child0 := NewNodeWithConfig(config) 60 | child0.StyleSetWidth(80) 61 | child0.StyleSetHeight(40) 62 | child0.StyleSetMargin(EdgeTop, 10) 63 | child0.StyleSetMargin(EdgeBottom, 10) 64 | root.InsertChild(child0, 0) 65 | child1 := NewNodeWithConfig(config) 66 | child1.StyleSetWidth(80) 67 | child1.StyleSetHeight(40) 68 | child1.StyleSetMargin(EdgeBottom, 5) 69 | child1.StyleSetFlexShrink(1) 70 | root.InsertChild(child1, 1) 71 | 72 | CalculateLayout(root, 200, 100, DirectionLTR) 73 | 74 | assert.False(t, root.Layout.HadOverflow) 75 | } 76 | 77 | func TestHadOverflow_gets_reset_if_not_logger_valid(t *testing.T) { 78 | config, root := newHadOverflowTests() 79 | child0 := NewNodeWithConfig(config) 80 | child0.StyleSetWidth(80) 81 | child0.StyleSetHeight(40) 82 | child0.StyleSetMargin(EdgeTop, 10) 83 | child0.StyleSetMargin(EdgeBottom, 10) 84 | root.InsertChild(child0, 0) 85 | child1 := NewNodeWithConfig(config) 86 | child1.StyleSetWidth(80) 87 | child1.StyleSetHeight(40) 88 | child1.StyleSetMargin(EdgeBottom, 5) 89 | root.InsertChild(child1, 1) 90 | 91 | CalculateLayout(root, 200, 100, DirectionLTR) 92 | 93 | assert.True(t, root.Layout.HadOverflow) 94 | 95 | child1.StyleSetFlexShrink(1) 96 | 97 | CalculateLayout(root, 200, 100, DirectionLTR) 98 | 99 | assert.False(t, root.Layout.HadOverflow) 100 | } 101 | 102 | func TestSpacing_overflow_in_nested_nodes(t *testing.T) { 103 | config, root := newHadOverflowTests() 104 | child0 := NewNodeWithConfig(config) 105 | child0.StyleSetWidth(80) 106 | child0.StyleSetHeight(40) 107 | child0.StyleSetMargin(EdgeTop, 10) 108 | child0.StyleSetMargin(EdgeBottom, 10) 109 | root.InsertChild(child0, 0) 110 | child1 := NewNodeWithConfig(config) 111 | child1.StyleSetWidth(80) 112 | child1.StyleSetHeight(40) 113 | root.InsertChild(child1, 1) 114 | child1_1 := NewNodeWithConfig(config) 115 | child1_1.StyleSetWidth(80) 116 | child1_1.StyleSetHeight(40) 117 | child1_1.StyleSetMargin(EdgeBottom, 5) 118 | child1.InsertChild(child1_1, 0) 119 | 120 | CalculateLayout(root, 200, 100, DirectionLTR) 121 | 122 | assert.True(t, root.Layout.HadOverflow) 123 | } 124 | -------------------------------------------------------------------------------- /flex/issue5_test.go: -------------------------------------------------------------------------------- 1 | package flex 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestIssue5(t *testing.T) { 11 | config := NewConfig() 12 | config.Context = "test" 13 | 14 | // check that "padding" set with EdgeAll is printed out 15 | root := NewNodeWithConfig(config) 16 | root.StyleSetFlexDirection(FlexDirectionColumn) 17 | root.StyleSetHeightPercent(100) 18 | 19 | child := NewNodeWithConfig(config) 20 | child.StyleSetPadding(EdgeAll, 20) 21 | root.InsertChild(child, 0) 22 | 23 | CalculateLayout(root, Undefined, Undefined, DirectionLTR) 24 | 25 | w := &bytes.Buffer{} 26 | printer := NewNodePrinter(w, PrintOptionsLayout|PrintOptionsStyle|PrintOptionsChildren) 27 | printer.Print(root) 28 | got := w.String() 29 | exp := `