-
Notifications
You must be signed in to change notification settings - Fork 19
Layout class
This is an idea for Shoes 3.3.8 or beyond.
We define a new Shoes slot like 'thing' called 'layout' so we have flow, stack and layout. layout has many of the methods and style settings that flow does but it adds a couple of new ones.
class MyLayout
...
end
Shoes.app do
layout manager: MyLayout.new(), width: 400, height: 300 do
button "One"
para "Two"
end
end
The manager: hash argument should include an object. Shoes will call methods of that object/class at certain points. In computer terminology that means there is a protocol to follow. What the methods do depends on what the author wants. Shoes just requires they exist with the proper arguments and return the correct value.
class MyLayout
def initialize()
end
def setup(canvas, attributes)
end
def add(canvas, widget, attributes)
end
def remove(canvas, widget, position)
return true
end
def clear()
end
def finish()
end
end
This is the standard Ruby initialize for your class. Arguments can be what ever you like.
This is called after the layout slot's canvas has been created. The attributes argument is the styles hash. Note that hash keys are symbols. :width and :height for example. Be cautious. There may or may not be the key you want and the value may not be what you expect.
This method is called after the widget has been added to the layout canvas/slot contents array. Attributes that the user supplied are available. Note: you can send in an style argument that Shoes doesn't define but you can use, for example,
@ml = layout manager: MyLayout.new() do
button "Press Me:, name: 'widget-1'
para "User Info Demo", name: 'widget-2'
end
:name is not a Shoes style but in the add method you can get it name = attributes && attributes[:name]
. This is a feature. As a layout author, you can use this to establish a protocol with your layout user.
At some point, either here in add() or in finialize() you need to widget.move x,y
to the proper place in your layout. Until then, Shoes thinks this is flow slot. It's the move
that tells Shoes not to manage it and just leave it where it is. We depend this feature.
This is called before the widget is deleted from the canvas/slot at the given position. The position is probably the
numeric position in the contents array to the canvas. You can return nil if you don't want it deleted. That's not user friendly but it's your layout. Explicitly, return true
is recommended.
This method is called when all contents have gone away - when the slot/canvas has been deleted. An orderly Shoes quit will call this. You probably don't want to do anything here, but if you opened a SQL data base in initialize() or setup() you'd want to close it here.
This will be called when the layout user issues a 'finish' method call. If you do all the layout in add()/remove() then this method can be empty. However, if your layout requires knowing when to compute and move() all the widgets in the layout you would do that here.
DO NOT iterate over the attributes/style hash. It has keys that will crash Shoes it you try. Even inspect can crash Shoes.
At some point Shoes may include an internal layout manager when you don't provide your own layout manager class. That day is not here.
One way to approach the whole layout issue would be to have something like the user defined Shoes Widget only for slots. class MyLayout < Shoes::Slot
You would get all the slot methods add,clear,replace,hide to intercept if needed as well as contents array. The class author would need to supply a method for shoes_place_decide() to call for (re)sizing.
From the user perspective, and borrowing from Android constraint layout
require `shoes/contraints`
Shoes.app do
stack do
para "Demo"
@cl = constraint_layout width: 300, height: 200 do
b1 = button "one", width: 40
b2 = button "two", width: 80, height: 30
self.topleftpos b1,20,10
self.radius b1,b2,120
end
para "after"
end
end
shoes/constraints:
class constraint_layout << Shoes::Slot
attr_accessor :width, :height, :contraints
def initialize(hashargs)
@constraints = {}
end
def topleftpos (widget,top,left)
# initial position
@constraints[widget] = [:fixed, top, left]
widget.move_to(top,left)
end
def radius(wid1, wid2, degrees)
# initial position
@contraints[wid1] = [:radial ,wid2, degree]
ntop,nleft = compute_radial(wid1, degrees)
wid2.move_to(ntop,nleft)
end
# called from inside Shoes:
def resize(new_x, new_y, new_w, new_h)
recompute_all_xy(new_x, new_y, new_w, new_h)
@constraints.each do |wid, ary|
case ary[0]
when :fixed
x = @widgets[wid].x
y = @widgets[wid].y
wid.move_to(x,y)
when :radial
end
end
end
Of course that example is woefully wrong and incomplete - fixing it is not the question. The question is this API sufficient for writing Layouts?