package layouts import "image" import "git.tebibyte.media/sashakoshka/tomo" import "git.tebibyte.media/sashakoshka/tomo/theme" // Dialog arranges elements in the form of a dialog box. The first element is // positioned above as the main focus of the dialog, and is set to expand // regardless of whether it is expanding or not. The remaining elements are // arranged at the bottom in a row called the control row, which is aligned to // the right, the last element being the rightmost one. type Dialog struct { // If Gap is true, a gap will be placed between each element. Gap bool // If Pad is true, there will be padding running along the inside of the // layout's border. Pad bool } // Arrange arranges a list of entries into a dialog. func (layout Dialog) Arrange (entries []tomo.LayoutEntry, width, height int) { if layout.Pad { width -= theme.Padding() * 2 height -= theme.Padding() * 2 } controlRowWidth, controlRowHeight := 0, 0 if len(entries) > 1 { controlRowWidth, controlRowHeight = layout.minimumSizeOfControlRow(entries[1:]) } if len(entries) > 0 { entries[0].Position = image.Point { } if layout.Pad { entries[0].Position.X += theme.Padding() entries[0].Position.Y += theme.Padding() } mainHeight := height - controlRowHeight if layout.Gap { mainHeight -= theme.Padding() } mainBounds := entries[0].Bounds() if mainBounds.Dy() != mainHeight || mainBounds.Dx() != width { entries[0].Resize(width, mainHeight) } } if len(entries) > 1 { freeSpace := width expandingElements := 0 // count the number of expanding elements and the amount of free // space for them to collectively occupy for index, entry := range entries[1:] { if entry.Expand { expandingElements ++ } else { entryMinWidth, _ := entry.MinimumSize() freeSpace -= entryMinWidth } if index > 0 && layout.Gap { freeSpace -= theme.Padding() } } expandingElementWidth := 0 if expandingElements > 0 { expandingElementWidth = freeSpace / expandingElements } // determine starting position and dimensions for control row x, y := 0, height - controlRowHeight if expandingElements == 0 { x = width - controlRowWidth } if layout.Pad { x += theme.Padding() y += theme.Padding() } height -= controlRowHeight // set the size and position of each element in the control row for index, entry := range entries[1:] { if index > 0 && layout.Gap { x += theme.Padding() } entries[index + 1].Position = image.Pt(x, y) entryWidth := 0 if entry.Expand { entryWidth = expandingElementWidth } else { entryWidth, _ = entry.MinimumSize() } x += entryWidth entryBounds := entry.Bounds() if entryBounds.Dy() != controlRowHeight || entryBounds.Dx() != entryWidth { entry.Resize(entryWidth, controlRowHeight) } } } } // MinimumSize returns the minimum width and height that will be needed to // arrange the given list of entries. func (layout Dialog) MinimumSize ( entries []tomo.LayoutEntry, ) ( width, height int, ) { if len(entries) > 0 { mainChildHeight := 0 width, mainChildHeight = entries[0].MinimumSize() height += mainChildHeight } if len(entries) > 1 { if layout.Gap { height += theme.Padding() } additionalWidth, additionalHeight := layout.minimumSizeOfControlRow(entries[1:]) height += additionalHeight if additionalWidth > width { width = additionalWidth } } if layout.Pad { width += theme.Padding() * 2 height += theme.Padding() * 2 } return } // FlexibleHeightFor Returns the minimum height the layout needs to lay out the // specified elements at the given width, taking into account flexible elements. func (layout Dialog) FlexibleHeightFor ( entries []tomo.LayoutEntry, width int, ) ( height int, ) { if layout.Pad { width -= theme.Padding() * 2 } if len(entries) > 0 { mainChildHeight := 0 if child, flexible := entries[0].Element.(tomo.Flexible); flexible { mainChildHeight = child.FlexibleHeightFor(width) } else { _, mainChildHeight = entries[0].MinimumSize() } height += mainChildHeight } if len(entries) > 1 { if layout.Gap { height += theme.Padding() } _, additionalHeight := layout.minimumSizeOfControlRow(entries[1:]) height += additionalHeight } if layout.Pad { height += theme.Padding() * 2 } return } // TODO: possibly flatten this method to account for flexible elements within // the control row. func (layout Dialog) minimumSizeOfControlRow ( entries []tomo.LayoutEntry, ) ( width, height int, ) { for index, entry := range entries { entryWidth, entryHeight := entry.MinimumSize() if entryHeight > height { height = entryHeight } width += entryWidth if layout.Gap && index > 0 { width += theme.Padding() } } return }