libui/redo/osxaltest/box.swift

176 lines
5.1 KiB
Swift

// 7 august 2015
import Cocoa
struct BoxControl {
var c: Control
var stretchy: Bool
var horzHuggingPri: NSLayoutPriority
var vertHuggingPri: NSLayoutPriority
}
class Box : NSView, Control {
private var controls: [BoxControl]
private var parent: Control?
private var vertical: Bool
private var padded: Bool
private var primaryDirPrefix: String
private var secondaryDirPrefix: String
private var primaryOrientation: NSLayoutConstraintOrientation
private var secondaryOrientation: NSLayoutConstraintOrientation
// we implement a lack of stretchy controls by adding a stretchy view at the end of the view list when we assemble layouts
// this is that view
private var noStretchyView: NSView
init(vertical: Bool, padded: Bool) {
self.controls = []
self.parent = nil
self.vertical = vertical
self.padded = padded
self.primaryDirPrefix = "H:"
self.secondaryDirPrefix = "V:"
self.primaryOrientation = NSLayoutConstraintOrientation.Horizontal
self.secondaryOrientation = NSLayoutConstraintOrientation.Vertical
if self.vertical {
self.primaryDirPrefix = "V:"
self.secondaryDirPrefix = "H:"
self.primaryOrientation = NSLayoutConstraintOrientation.Vertical
self.secondaryOrientation = NSLayoutConstraintOrientation.Horizontal
}
self.noStretchyView = NSView(frame: NSZeroRect)
self.noStretchyView.translatesAutoresizingMaskIntoConstraints = false
// make the view stretchy in both directions
// you can tell this is correct by synthesizing an Add() in your head; see below
setHorzHuggingPri(self.noStretchyView, myNSLayoutPriorityDefaultLow)
setVertHuggingPri(self.noStretchyView, myNSLayoutPriorityDefaultLow)
super.init(frame: NSZeroRect)
self.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError("can't use this constructor, sorry")
}
func Add(control: Control, _ stretchy: Bool) {
var c: BoxControl
var view = control.View()
c = BoxControl(
c: control,
stretchy: stretchy,
horzHuggingPri: horzHuggingPri(view),
vertHuggingPri: vertHuggingPri(view))
self.addSubview(view)
self.controls.append(c)
// if a control is stretchy, it should not hug in the primary direction
// otherwise, it should *forcibly* hug
if c.stretchy {
setHuggingPri(view, myNSLayoutPriorityDefaultLow, self.primaryOrientation)
} else {
// TODO will default high work?
setHuggingPri(view, myNSLayoutPriorityRequired, self.primaryOrientation)
}
// make sure controls don't hug their secondary direction so they fill the width of the view
setHuggingPri(view, myNSLayoutPriorityDefaultLow, self.secondaryOrientation)
self.relayout()
}
func View() -> NSView {
return self
}
func SetParent(p: Control) {
self.parent = p
}
// TODO do we still need to set hugging? I think we do for stretchy controls...
// TODO try unsetting spinbox intrinsics and seeing what happens
private func relayout() {
var constraint: String
if self.controls.count == 0 {
return
}
self.removeConstraints(self.constraints)
// first collect the views
var views = [String: NSView]()
var n = 0
var firstStretchy = -1
var metrics = [String: CGFloat]()
for c in self.controls {
views["view\(n)"] = c.c.View()
var s = fittingAlignmentSize(c.c.View())
metrics["view\(n)width"] = s.width
metrics["view\(n)height"] = s.height
if firstStretchy == -1 && c.stretchy {
firstStretchy = n
}
n++
}
// if there are no stretchy controls, we must add the no-stretchy view
// if there are, we must remove it
if firstStretchy == -1 {
if self.noStretchyView.superview == nil {
self.addSubview(self.noStretchyView)
}
views["noStretchyView"] = self.noStretchyView
} else {
if self.noStretchyView.superview != nil {
self.noStretchyView.removeFromSuperview()
}
}
// next, assemble the views in the primary direction
// they all go in a straight line
constraint = "\(self.primaryDirPrefix)|"
for i in 0..<n {
if self.padded && i != 0 {
constraint += "-"
}
constraint += "[view\(i)"
// implement multiple stretchiness properly
if self.controls[i].stretchy && i != firstStretchy {
constraint += "(==view\(firstStretchy))"
}
// if the control is not stretchy, restrict it to the fitting size
if !self.controls[i].stretchy {
if self.vertical {
constraint += "(==view\(i)height)"
} else {
constraint += "(==view\(i)width)"
}
}
constraint += "]"
}
if firstStretchy == -1 { // don't space between the last control and the no-stretchy view
constraint += "[noStretchyView]"
}
constraint += "|"
var constraints = mkconstraints(constraint, metrics, views)
self.addConstraints(constraints)
// next: assemble the views in the secondary direction
// each of them will span the secondary direction
for i in 0..<n {
constraint = "\(self.secondaryDirPrefix)|[view\(i)]|"
var constraints = mkconstraints(constraint, nil, views)
self.addConstraints(constraints)
}
if firstStretchy == -1 { // and again to the no-stretchy view
constraint = "\(self.secondaryDirPrefix)|[noStretchyView]|"
var constraints = mkconstraints(constraint, nil, views)
self.addConstraints(constraints)
}
}
}