howl.ui.Window

local win

before_each ->
  win = Window!
  win\realize!

add_view(view [, placement, anchor])

adds the specified view

win\add_view Gtk.Label!
assert.equals 1, #win.views

returns a table containing x, y, width, height and the view

label = Gtk.Label!
assert.same { x: 1, y: 1, width: 1, height: 1, view: label }, win\add_view label

adds the new view to the right of the currently focused one by default

entry = Gtk.Entry!
win\add_view entry
entry\grab_focus!
label = Gtk.Label!
assert.same { x: 2, y: 1, width: 1, height: 1, view: label }, win\add_view label

(when placement is specified)

local view, entry

before_each ->
  entry = Gtk.Entry!
  win\add_view entry
  entry\grab_focus!
  view = Gtk.Entry!

"right_of" places the view on the right side of the focused child

assert.same { x: 2, y: 1, width: 1, height: 1, :view }, win\add_view view, 'right_of'

"left_of" places the view on the left side of the focused child

assert.same { x: 1, y: 1, width: 1, height: 1, :view }, win\add_view view, 'left_of'

"above" places the view above the focused child

assert.same { x: 1, y: 1, width: 1, height: 1, :view }, win\add_view view, 'above'
assert.same { x: 1, y: 2, width: 1, height: 1, view: entry }, win\get_view entry

"below" places the view below the focused child

assert.same { x: 1, y: 2, width: 1, height: 1, :view }, win\add_view view, 'below'

allows specifying the relative view to use with placement

win\add_view view, 'below'
next_view = Gtk.Label!
assert.same { x: 1, y: 2, width: 1, height: 1, view: next_view }, win\add_view next_view, 'left_of', view

creates new columns as needed

win\add_view view, 'right_of'
next_view = Gtk.Label!
assert.same { x: 2, y: 1, width: 1, height: 1, view: next_view }, win\add_view next_view, 'left_of', view
assert.same { 1, 2, 3 }, [v.x for v in *win.views]

remove_view(view)

removes the specified view

label = Gtk.Label!
win\add_view label
win\remove_view label
assert.equals 0, #win.views

removes the currently focused child if view is nil

entry = Gtk.Entry!
win\add_view entry
entry\grab_focus!
win\remove_view!
assert.equals 0, #win.views

raises an error if view is nil and no child has focus

label = Gtk.Label!
win\add_view label
assert.raises 'remove', -> win\remove_view!

set focus on its later sibling is possible

left = Gtk.Entry!
middle = Gtk.Entry!
right = Gtk.Entry!
win\add_view left
win\add_view middle
win\add_view right
middle\grab_focus!
win\remove_view middle
assert.is_true right.is_focus

set focus on its earlier sibling if no later sibling exists

left = Gtk.Entry!
middle = Gtk.Entry!
right = Gtk.Entry!
win\add_view left
win\add_view middle
win\add_view right
right\grab_focus!
win\remove_view right
assert.is_true middle.is_focus

.views

is a table of view tables, containing x, y and the view itself

label = Gtk.Label!
win\add_view label
assert.same { { x: 1, y: 1, width: 1, height: 1, view: label } }, win.views

ordered ascendingly

entry = Gtk.Entry!
win\add_view entry
entry\grab_focus!

l1 = Gtk.Label!
l2 = Gtk.Label!
win\add_view l1, 'left_of'
win\add_view l2, 'below'
assert.same { l1, entry, l2 }, [v.view for v in *win.views]

.current_view

is nil if no child is currently focused

assert.is_nil, win.current_view

is a table containing x, y and the view for the currently focused view

e1 = Gtk.Entry!
e2 = Gtk.Entry!
win\add_view e1
win\add_view e2

e1\grab_focus!
assert.same { x: 1, y: 1, width: 1, height: 1, view: e1 }, win.current_view

e2\grab_focus!
assert.same { x: 2, y: 1, width: 1, height: 1, view: e2 }, win.current_view

siblings(view, wraparound)

returns an empty table if there are no siblings

v1 = Gtk.Entry!
  win\add_view v1
  assert.same {}, win\siblings v1

defaults to the currently focused child if view is not provided

v1 = Gtk.Entry!
  win\add_view v1
  v1\grab_focus!
  assert.same {}, win\siblings!

returns an empty table if view is not provided and no child is focused

assert.same {}, win\siblings!

(when wraparound is false)

returns a table of siblings for the specified view when present

left = Gtk.Entry!
 right = Gtk.Entry!
 bottom = Gtk.Entry!
 win\add_view left
 win\add_view right, 'right_of', left
 win\add_view bottom, 'below', left

 assert.same { right: right, down: bottom }, win\siblings left, false
 assert.same { left: left, down: bottom }, win\siblings right, false
 assert.same { up: left }, win\siblings bottom, false

(when wraparound is true)

returns a table of siblings for the specified view in a wraparound fashion

left = Gtk.Entry!
  right = Gtk.Entry!
  bottom = Gtk.Entry!
  win\add_view left
  win\add_view right, 'right_of', left
  win\add_view bottom, 'below', left

  assert.same { left: bottom, right: right, up: bottom, down: bottom }, win\siblings left, true
  assert.same { left: left, right: bottom, up: bottom, down: bottom }, win\siblings right, true
  assert.same { left: right, right: left, up: left, down: left }, win\siblings bottom, true

column reflowing

local left, right, bottom

before_each ->
  left = Gtk.Entry!
  right = Gtk.Entry!
  bottom = Gtk.Entry!
  win\add_view left
  win\add_view right
  right\grab_focus!
  win\add_view bottom, 'below'

single columns as expanded as necessary

assert.same { x: 1, y: 2, width: 2, height: 1, view: bottom }, win\get_view bottom

columns to the right are adjusted after a remove of a left column

win\remove_view left
assert.same { x: 1, y: 1, width: 1, height: 1, view: right }, win\get_view right

columns to the left are adjusted after a remove of a right column

win\remove_view right
assert.same { x: 1, y: 1, width: 1, height: 1, view: left }, win\get_view left

rows are adjusted after removal of a middle column

middle = Gtk.Entry!
win\add_view middle, 'right_of', left
win\remove_view middle
assert.same { x: 1, y: 1, width: 1, height: 1, view: left }, win\get_view left
assert.same { x: 2, y: 1, width: 1, height: 1, view: right }, win\get_view right

(resource management)

added views are not anchored

v = Gtk.Entry!
views = setmetatable {v}, __mode: 'v'
win\add_view v
v = nil
collectgarbage!
assert.is_nil views[1]