shoes4
shoes4 copied to clipboard
Add drag-and-drop functionality
Let's explore how to add drag-and-drop to Shoes. How should the DSL read? I think it's technically doable in Swt.
Phew. Hard nut to crack.
From Squeak Smalltalk I know an object oriented approach. There the objects have methods like "canBeDroppedOn: anObject" and "accepts: anObject" (method names are not accurate, could look it up though).
how about kind of:
drag_and_drop element, on: container
or something like that? First thing of the top of my head.
+1 for "canBeDroppedOn: anObject" and "accepts: anObject" In general, I find smalltalk to always be the right-headed object-orientation example.
Peter Fitzgibbons (847) 859-9550 Email: [email protected] IM GTalk: peter.fitzgibbons IM AOL: [email protected]
On Thu, Oct 25, 2012 at 3:16 PM, Tobias Pfeiffer [email protected]:
Phew. Hard nut to crack.
From Squeak Smalltalk I know an object oriented approach. There the objects have methods like "canBeDroppedOn: anObject" and "accepts: anObject" (method names are not accurate, could look it up though).
how about kind of:
drag_and_drop element, on: container
or something like that? First thing of the top of my head.
— Reply to this email directly or view it on GitHubhttps://github.com/shoes/shoes4/issues/139#issuecomment-9792442.
The problem however is that we'd need objects for that... and so far shoes, imo, is more kind of a markup language/DSL. I never wrote my own rectangle or something... that doesn't seem to be too common or am I wrong?
Oh and now I looked it up. I was a bit wrong, there is no "can be dropped on" - just whether a morph is accepted. But by default in Squeak Smalltalk you can drag and drop just about anything. Oh for understanding: Allg raphical objects in squeak smalltalk are Morphs.
ReceiverMorph»wantsDroppedMorph: aMorph event: anEvent
(return a boolean here)
Through different methods you can specify what happens if the Morph can't be dropped here (for instance send it back to the starting position).
What I'm talking about is chapter 11.7 of the free (Squeak by Example PDF)[http://www.squeakbyexample.org/] - if anyone wants further info or something :-)
What about something like this:
Shoes.app do
edit_box width: 10, dragndrop: lambda { |obj| ... }
end
@davorb I like where you're going with this. We could even offer shortcuts for common operations
Shoes.app do
image "cat3.jpg", width: 100, height: 100, drag: true
stack do
title "Cats"
flow :drop => :append do
image "cat1.jpg"
image "cat2.jpg"
end
end
end
@wasnotrice That's genius!
Yep, agreed... genius! +1 for the DSL.
"droppables" are identified by drag: true, "drop targets" are identified by
drop:
What would be the potential drop-opts ?
Peter Fitzgibbons (847) 859-9550 Email: [email protected] IM GTalk: peter.fitzgibbons IM AOL: [email protected]
On Mon, Oct 29, 2012 at 4:39 AM, Davor Babić [email protected]:
@wasnotrice https://github.com/wasnotrice That's genius!
— Reply to this email directly or view it on GitHubhttps://github.com/shoes/shoes4/issues/139#issuecomment-9860470.
Oh neat.
Potential drop-outs would be primarily text, I think. On Oct 29, 2012 6:40 AM, "Peter Fitzgibbons" [email protected] wrote:
Yep, agreed... genius! +1 for the DSL.
"droppables" are identified by drag: true, "drop targets" are identified by drop:
What would be the potential drop-opts ?
Peter Fitzgibbons (847) 859-9550 Email: [email protected] IM GTalk: peter.fitzgibbons IM AOL: [email protected]
On Mon, Oct 29, 2012 at 4:39 AM, Davor Babić [email protected]:
@wasnotrice https://github.com/wasnotrice That's genius!
— Reply to this email directly or view it on GitHub< https://github.com/shoes/shoes4/issues/139#issuecomment-9860470>.
— Reply to this email directly or view it on GitHubhttps://github.com/shoes/shoes4/issues/139#issuecomment-9862023.
@wasnotrice genius indeed :-D
Hmmm. Now that I look more closely at Shoes 3, I see that Slot#append
only takes a block. In other words, you can't append an existing element--you can only add a new one. Are there any drawbacks to adding an optional argument to #append
, #prepend
?
We would definitely want to allow a lambda as a drop-opt, as @davorb sketched initially.
flow :drop => { |obj| para "Yum! Thanks for the #{obj.name}" }
It's possible that this is too simplistic to work, but let's give it a shot and see if we can make something usable that is also simple.
:shoe:
Sorry for my late reply. The discussion is interesting. But, I think that it's better to reconsider what a drag-and-drop function is and how to use.
Shoes has already some methods, i.e. click, release, motion, mouse, etc. So, within a Shoes window, users can write the code of drag-and-drop by theirselves as their own Shoes apps. For example, a tiny Shoes app Ruby Magnets on Shoes. Watch a demo.
If the drag-and-drop function is that dragging something (e.g. icons) from desktop and dropping them on a Shoes app window or dragging some elements (e.g. image, oval, rect, text, etc.) from a Shoes app window and dropping them on desktop, Shoes doesn't have the feature.
I'm not sure the feature is necessary for Shoes, though...
Shoes doesn't come with tabbed controls or toolbars. Shoes is a tiny toolkit, remember? ;-)
@ashbb Thanks for Ruby Magnets on Shoes. I had not seen that before. Nice work!
I see how you wrote drag and drop yourself using #click
, #motion
, and #release
. I agree it can be done by the user. But ordinary things are easy in Shoes. Maybe dragging should be easy?
Also, maybe you know the answer to this. In your Ruby Magnets, when you drag something, it doesn't get added to the slot where you drop it. Can users write code in their apps so that when they drop something, it gets appended to a different slot?
@wasnotrice Are you looking for #contents
? How about the following snippet? Watch the demo. Red and Purple Shoes doesn't work well for now, though. :(
require 'green_shoes'
Shoes.app do
@f1 = flow do
background green
para 'This is green slot', width: 150
@img = image File.join(DIR, '../static/gshoes-icon.png')
@img.click{@flag = true}
@img.release do
@flag = false
if @img.top < @f1.height
@f2.contents.delete @img
@f1.contents.push @img
else
@f1.contents.delete @img
@f2.contents.push @img
end
flush
end
end
@f2 = flow do
background yellow
para 'This is yellow slot', width: 150
end
motion{|l, t| @img.move(l, t) if @flag}
end
@ashbb I like it! But this would be so much easier :)
Shoes.app do
@f1 = flow do
background green
para 'This is green slot', width: 150
@img = image File.join(DIR, '../static/gshoes-icon.png'), drag: true
end
@f1.drop do |obj, target|
target.append(obj) #appending an element also removes it from its old parent
end
@f2 = flow do
background yellow
para 'This is yellow slot', width: 150
end
end
I have concerns about using #contents
directly. I think this should be a read-only list.
Imagine that both flows had that drop block added ;)
@wasnotrice
this would be so much easier :)
Yeah, I agree it's much easier.
But,...
I have concerned about using
#contents
directly. I think this should be a read-only list.
If #contents
is an array, user can edit the data within the array freely...
For example:
class Slot
def initialize
@contents = %w[a b c]
end
attr_reader :contents
end
s = Slot.new
s.contents.delete 'a'
p s.contents #=> ["b", "c"]
@ashbb
If #contents is an array, user can edit the data within the array freely...
True, if we expose the actual array. But if we only expose a copy:
class Slot
def contents
@contents.dup
end
end
s = Slot.new
s.contents.delete 'a'
p s.contents #=> ["a", "b", "c"]
Now the user has to work through the Shoes API to change the contents of the slot. This is good. Look what goes wrong in this Shoes4 snippet:
Shoes.app width: 600, height: 200 do
f1 = flow do
para "hello"
para "world"
end
stack do
para "f1: #{f1}"
p1 = f1.contents.delete_at(0)
f1.contents.each { |e| para "#{e} (#{e.text}), parent: #{e.parent}" }
para "p1's parent: #{p1.parent}"
end
end

What should p1's parent be? If the user can manipulate @contents
, how can we manage that?
@wasnotrice #dup
... oh, yeah, but really need to do that? Give the users freedom! :-P :-D
Look what goes wrong in this Shoes4 snippet:
Umm, looks bad. The "hello" isn't erased.
What should p1's parent be?
p1 is "hello". So, p1's parent should be f1.
If the user can manipulate
@contents
, how can we manage that?
If you want to erase the "hello", you have to do not only remove it from f1's contents but also delete the swt-paint-listener.
I think user can do that if we support #clear
method for Shoes::TextBlock class. But at that time, user doesn't have to manipulate @contents
directly. ;-)
I agree with @ashbb, access to contents is freedom, operations on contents are dependent upon the implementation framework. I think if we .frozen the result of #contents then the usage will be obviated... can't change a frozen object.
A comment in code here will be very important... make note that the returned object is frozen and in practice cannot be modified because the @contents are not directly synced with the GUI Implementation Framework (ie. SwtShoes). Use the GUI framework operations to manipulate the objects you find in #contents.
Thoughts?
Peter Fitzgibbons (847) 859-9550 Email: [email protected] IM GTalk: peter.fitzgibbons IM AOL: [email protected]
On Tue, Nov 6, 2012 at 6:06 AM, ashbb [email protected] wrote:
@wasnotrice https://github.com/wasnotrice #dup ... oh, yeah, but really need to do that? Give the users freedom! :-P :-D
Look what goes wrong in this Shoes4 snippet:
Umm, looks bad. The "hello" isn't erased.
What should p1's parent be?
p1 is "hello". So, p1's parent should be f1.
If the user can manipulate @contents, how can we manage that?
If you want to erase the "hello", you have to do not only remove it from f1's contents but also delete the swt-paint-listener.
I think user can do that if we support #clear method for Shoes::TextBlock class. But at that time, user doesn't have to manipulate @contentsdirectly. ;-)
— Reply to this email directly or view it on GitHubhttps://github.com/shoes/shoes4/issues/139#issuecomment-10108222.
@ashbb I'm all for freedom :D
My point is that when a user manipulates #contents
directly, we can't clean up the display like we should. It's better to provide DSL methods for adding/removing objects to slots. But I'm not sure it will work ;)
Oops! I know that #remove
will work. I'm not sure whether it will work well to move an object from one slot to another (drag-and-drop)
Doesn't move in general require a destroy/build of the gui object?
Peter Fitzgibbons (847) 859-9550 Email: [email protected] IM GTalk: peter.fitzgibbons IM AOL: [email protected]
On Wed, Nov 7, 2012 at 12:42 PM, Eric Watson [email protected]:
Oops! I know that #remove will work. I'm not sure whether it will work well to move an object from one slot to another (drag-and-drop)
— Reply to this email directly or view it on GitHubhttps://github.com/shoes/shoes4/issues/139#issuecomment-10159771.
Doesn't move in general require a destroy/build of the gui object?
For moving from one container to another, yes.
@wasnotrice @pjfitzgibbons and folks,
1st:
It's better to provide DSL methods for adding/removing objects to slots.
Both in Red Shoes and current Shoes4, the elements moved or created with explicit position (ex. image(path).move(l, t) or oval(l, t, w)) don't belong to any slots. So, I agree to provide DSL methods for adding elements to slots. How about the following?
slot = flow
img = image(path)
slot.append img
The #remove
method disposes the element and removes it from the slot's contents.
2nd: about "drag-and-drop". I agree with @wasnotrice's DSL.
slot = flow
img = image(path, drag: true)
slot.drop{|obj, target| target.append obj}
But, I'm still not sure whether it's needed... Personally, I think that it's better to leave drag-and-drop function to Shoes app developers. Of course, I don't have any objection to providing drag-and-drop DSL. :)