-
Notifications
You must be signed in to change notification settings - Fork 28
ShoesSlotsSelf.md
In Shoes, there is a single Shoes::App object and it's the "self" everywhere. For instance:
def printish(msg)
$stderr.puts msg
File.open("/tmp/shoesy_stuff.txt", "a") do |f|
f.write(msg + "\n")
end
end
Shoes.app do
printish "Self top: #{self.inspect}" # It's an instance of Shoes::App, yes
stack do
printish "Self in stack: #{self.inspect}" # Yup, here too
end
button("Clickity") do
alert("You clicked me!")
printish "Self in button handler: #{self.inspect}" # And here too
end
end
Here's what I see from Shoes3 in that logfile:
Self top: (Shoes::Types::App "Shoes")
Self in stack: (Shoes::Types::App "Shoes")
Self in button handler: (Shoes::Types::App "Shoes")
Note: as of this writing in June 2023, Scarpe does not do this. Scarpe has several selves. This is a problem with some examples, and an incompatibility.
You can find other types of objects. For instance, the object returned by "button" there inspects as "(Shoes::Types::Button)". And you need an object that knows which button it belongs to so it can be used to change button text, set up new click handlers and so on.
Based on both Shoes-Core and Shoes3, I'll say: when a new slot object is created, it "pushes" itself onto a stack of slots. And then newly-created widgets can be added to the current "uppermost" slot, such as the stack you're currently creating.
You might reasonably say, wait, if a slot just pushes itself and first and pops itself after, can we add anything to it after its block is done? In Shoes3 yeah, definitely.
Here, look:
Shoes.app do
para "Top"
stack do
title "In stack!"
@f = flow do
button "Clickity" do
para "Wherity?"
@f.para "Addity"
end
para "Hrm"
puts "Flow: " + @f.inspect
end
end
end
In the button handler for "Clickity", notice that we add two paras. One goes after the Clickity-button in the flow and adds sidewise. But the other ("Wherity?") shows up at the bottom, after the stacks and flows. So just like we can save a button to mess with it later, we can save a reference to a slot (stack, flow, etc) to mess with it later. But that block still runs with "self" being your app. But your app remembers the current slot. To mess with a slot after its block is finished, you save a reference to it. Just like to mess with a button or para after you make it, save a reference to it.
This is a little confusing because:
- when you call "button" in the block of a slot, the button shows up in that slot
- calling "button" on the object returned by that slot will make the button show up in that slot
- but when you call "button" inside that block, you're calling it on your Shoes::App, not the slot's own object
Because Shoes::App is so tied through the API, the various Shoes apps all seem to distinguish between Shoes::App-the-DSL-object and the internal app. Though they all do it in slightly different ways. Shoes4 uses Shoes::App/Shoes::InternalApp. Shoes3 uses Shoes::App/Shoes::Canvas. Scarpe uses App/DocumentRoot.
This makes some sense. Since Scarpe::App is a DSL and interface object, some of the heavy lifting is likely to be better done elsewhere.
So if that's Shoes::App and various Shoes slots, how do Widgets work? A slot (e.g. a Shoes::Flow) inherites from Shoes::Widget. But you can also create a widget outside Shoes::App. How does that work?
Here, let's try this in Shoes3:
File.unlink("/tmp/shoesy_stuff.txt") rescue nil
def printish(msg)
$stderr.puts msg
File.open("/tmp/shoesy_stuff.txt", "a") do |f|
f.write(msg + "\n")
end
end
class Twinsies < Shoes::Widget
def initialize(text) # In Shoes4 this changes to initialize_widget
printish "Whoah, where are we? #{self.inspect} #{self.class} #{self.class.ancestors}"
para text
para em(text)
end
end
Shoes.app do
twinsies "Hello!"
end
This shows us that when we call para there, we're in a Twinsies object instance, of class Twinsies, and that inherits from Shoes::Widget, Shoes::Canvas, Shoes::Basic and more. But basically, it seems to be a Shoes slot (remember a Widget acts like a Flow for its contents), and the self is the slot object (Twinsies), not the Shoes::App.
Which would imply that you cannot use your instance variables from your Shoes.app in widgets. They get their own instance variables and can't share them.
What counts as a slot? Can only slots have children?
It looks a lot like a "slot" -- which can include, but is not limited to, stacks and flows -- is mostly a GTK+/Cairo-level abstraction. As a result, a "slot" is mostly a coordinate system for child widgets. This is part of why having a "shape" makes sense. You often want a sub-coordinate-system for multiple art widgets together. It just also happens to be squashed together with CSG union shapes.