Skip to content

Commit

Permalink
Add move. Closes #8
Browse files Browse the repository at this point in the history
This adds move for Shape, Oval, and Button. Shoes-level implementation
and an SWT gui implementation. Oval is straghtforward. Shape is
somewhat complicated because it moves all elements of the shape. I
believe this is a correct, but not optimized implementation.

Button is also complicated because Buttons can be laid out in slots.
The Button is removed from the slot's layout, so the remaining elements
can reflow. The Button's gui element is disposed, and a new gui element
is created and added to a new Swt::Composite layer so it is out of the
layout flow. This only happens on the first move. Once the Button is in
a container without a layout, it continues to use the same gui element.

Note that in this commit, the move method for button is on the button
class itself. It belongs somewhere else, but I wasn't yet sure what else
would have the same move semantics as button.

* Add NO_BACKGROUND so layers are transparent. This applies to
  all flow objects
* Adjust framerate to approach Red Shoes. It's still not a perfect
  match, but it's closer.
* Add new working sample: simple-move.rb
  • Loading branch information
wasnotrice committed May 28, 2012
1 parent 3b20b1a commit 5008464
Show file tree
Hide file tree
Showing 24 changed files with 146 additions and 44 deletions.
2 changes: 1 addition & 1 deletion lib/shoes/animation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def initialize gui_container, *opts, &blk
@style = opts.last.class == Hash ? opts.pop : {}
@style[:framerate] = opts.first if opts.length == 1
@framerate = @style[:framerate] || 24
super gui_container, opts, &blk
super gui_container, @style, &blk
end

attr_reader :framerate
Expand Down
1 change: 1 addition & 0 deletions lib/shoes/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def initialize(opts={}, &blk)

self.opts = opts

@app = self
@style = DEFAULT_STYLE

gui_init
Expand Down
12 changes: 8 additions & 4 deletions lib/shoes/button.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
require 'shoes/common_methods'

module Shoes
class Button
include Shoes::CommonMethods

attr_accessor :gui_container, :click_event_lambda
attr_accessor :gui_element
attr_accessor :text, :width, :height
attr_accessor :text

def initialize(gui_container, text = 'Button', opts={}, click_event_lambda = nil)
self.gui_container = gui_container
self.click_event_lambda = click_event_lambda
self.text = text
self.height = opts[:height]
self.width = opts[:width]

@app = opts[:app]
@height = opts[:height]
@width = opts[:width]

gui_button_init
end

Expand Down
6 changes: 6 additions & 0 deletions lib/shoes/common_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ def toggle

end

# Moves an element to a specific pixel position. The element is still in the slot,
# but will no longer be stacked or flowed with the other stuff in the slot.
def move(left, top)
@left, @top = left, top
end

# displace(left: a number, top: a number) » self
# Displacing an element moves it. But without changing the layout around it.
def displace(left, top)
Expand Down
4 changes: 4 additions & 0 deletions lib/shoes/element_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ module ElementMethods
#end

def flow(opts = {}, &blk)
opts.merge! :app => @app
swt_flow = Shoes::Flow.new(self, self.gui_container, opts, blk)
end


def button(text, opts={}, &blk)
opts.merge! :app => @app
button = Shoes::Button.new(self.gui_container, text, opts, blk)
#@elements[button.to_s] = button
#button
Expand All @@ -54,6 +56,8 @@ def button(text, opts={}, &blk)
# end
#
def animate(opts = {}, &blk)
opts = {:framerate => opts} unless opts.is_a? Hash
opts.merge! :app => @app
animation = Shoes::Animation.new(gui_container, opts, &blk)
end

Expand Down
3 changes: 2 additions & 1 deletion lib/shoes/flow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def initialize(parent_container, parent_gui_container, opts={}, blk = nil)
self.width = opts['width']
self.height = opts['height']
self.margin = opts['margin']
@app = opts['app']

self.blk = blk

Expand All @@ -29,4 +30,4 @@ def initialize(parent_container, parent_gui_container, opts={}, blk = nil)

end
end
end
end
40 changes: 28 additions & 12 deletions lib/shoes/shape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ def initialize(opts = {}, blk = nil)

@blk = blk

@left = @style[:left] || 0
@top = @style[:top] || 0
@width = @style[:width] || 0
@height = @style[:height] || 0

# Initialize current point to (left, top)
@x, @y = @left, @top

# Component shapes
@components = []

Expand All @@ -43,19 +35,19 @@ def initialize(opts = {}, blk = nil)
end

def left
@components.map(&:left).min
@left || @components.map(&:left).min || 0
end

def top
@components.map(&:top).min
@top || @components.map(&:top).min || 0
end

def right
@components.map { |c| c.left + c.width }.max
@right || @components.map { |c| c.left + c.width }.max || left
end

def bottom
@components.map { |c| c.top + c.height }.max
@bottom || @components.map { |c| c.top + c.height }.max || top
end

def width
Expand All @@ -65,5 +57,29 @@ def width
def height
(top - bottom).abs
end

# Moves the shape
#
# Moves each component so bounds calculations still work.
def move(left, top)
relative_left = offset(self.left, left)
relative_top = offset(self.top, top)
@components.each do |c|
c_left = c.left
c_top = c.top
c.move(c_left + relative_left, c_top + relative_top)
end
@left, @top, @right, @bottom = left, top, nil, nil
end

# Gives the relative offset of the new position from original position
#
# Returns a value that should be added to the current position in order to
# move to the new position.
def offset(original, new)
relative = (new - original).abs
relative = -relative if new < original
relative
end
end
end
1 change: 1 addition & 0 deletions lib/shoes/timer_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class TimerBase
def initialize gui_container, opts, &blk
@gui_container = gui_container
@blk = blk
@app = opts[:app]
@stopped = false
gui_init
end
Expand Down
6 changes: 3 additions & 3 deletions lib/swt_shoes/animation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ def gui_init
task = Proc.new do
@blk.call(@current_frame)
@current_frame += 1
@gui_container.redraw
Swt.display.timer_exec 1000 / @framerate, task
@app.gui_container.redraw
Swt.display.timer_exec (2000 / @framerate), task
end
Swt.display.timer_exec 1000 / @framerate, task
Swt.display.timer_exec (2000 / @framerate), task
end

#def stop
Expand Down
24 changes: 24 additions & 0 deletions lib/swt_shoes/button.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ def gui_button_init
button.pack
end

def move(left, top)
super(left, top)
unless gui_element.disposed?
# If this element is part of a layout, we need to pop it into its own
# composite layer before moving it, so the rest of of the elements in
# the layout can reflow.
if gui_container.get_layout
old_gui_container = self.gui_container
self.gui_container = Swt::Widgets::Composite.new(@app.gui_container, Swt::SWT::NO_BACKGROUND)
self.gui_element.dispose
self.gui_container.set_layout nil
self.gui_element = Swt::Widgets::Button.new(gui_container, Swt::SWT::PUSH).tap do |button|
button.set_text(self.text)
button.add_selection_listener(self.click_event_lambda) if click_event_lambda
button.pack
end
self.gui_container.set_bounds(0, 0, @app.gui_container.size.x, @app.gui_container.size.y)
self.gui_container.move_above(old_gui_container)
old_gui_container.layout
end
self.gui_element.set_location left, top
self.gui_element.redraw
end
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/swt_shoes/flow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module SwtShoes
module Flow

def gui_flow_init
self.gui_container = container = Swt::Widgets::Composite.new(self.parent_gui_container, Swt::SWT::NONE)
self.gui_container = container = Swt::Widgets::Composite.new(self.parent_gui_container, Swt::SWT::NO_BACKGROUND)

# RowLayout is horizontal by default, wrapping by default
layout = Swt::Layout::RowLayout.new
Expand Down
23 changes: 21 additions & 2 deletions lib/swt_shoes/shape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,19 @@ def gui_init
# @gui_opts should be nil
if @gui_opts
@gui_container = @gui_opts[:container]
@gui_element = @gui_opts[:element] || Swt::Path.new(Shoes.display)
@gui_element = @gui_opts[:element] || Swt::Path.new(Swt.display)
#@transform.translate(-130, -100)
@gui_paint_callback = lambda do |event|
gc = event.gc
@transform = Swt::Transform.new(Swt.display) unless @transform
gc.setTransform(@transform)
gc.set_background self.fill.to_native
gc.fill_path(@gui_element)
gc.set_antialias Swt::SWT::ON
gc.set_foreground self.stroke.to_native
gc.set_line_width self.style[:strokewidth]
gc.draw_path(@gui_element)
@transform.dispose
end
@gui_container.add_paint_listener(@gui_paint_callback)
end
Expand All @@ -65,11 +69,26 @@ def move_to(x, y)
@x, @y = x, y
@gui_element.move_to(x, y)
end

def move(left, top)
super left, top
@transform = Swt::Transform.new(Swt.display)
@transform.translate(self.left, self.top)
end
end
end

module Shoes
class Shape
include SwtShoes::Shape
# This is a hack to allow methods in this class to override methods
# defined in Shoes::Shape. It would be better fixed by a different
# architecture. The self.extend doesn't work if the module has already
# been included by the class.
#include SwtShoes::Shape
alias :old_initialize :initialize
def initialize(opts = {}, blk = nil)
self.extend SwtShoes::Shape
old_initialize opts, blk
end
end
end
1 change: 1 addition & 0 deletions samples/README
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ Here's a list of the samples in this directory that work. Please
add to this list as you implement features!

simple-animate.rb
simple-move.rb
simple-sound.rb
simple-stripes.rb
14 changes: 9 additions & 5 deletions spec/shoes/button_spec.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
require "spec_helper"
require "shoes/spec_helper"

#require 'support/shared_examples_for_common_elements_spec'

describe Shoes::Button do

#it_should_behave_like "A Common Element"
let(:input_block) { Proc.new {} }
let(:input_opts) { {:width => 131, :height => 137, :margin => 143} }
subject { Shoes::Button.new("gui_container", "text", input_opts, input_block) }

it_behaves_like "movable object"

describe "initialize" do
it "should set accessors" do
input_block = lambda {}
input_opts = {:width => 131, :height => 137, :margin => 143}
button = Shoes::Button.new("gui_container", "text", input_opts, input_block)
button = subject
button.gui_container.should == "gui_container"
button.click_event_lambda.should == input_block
button.text.should == "text"
button.width.should == 131
button.height.should == 137
end
end
end
8 changes: 3 additions & 5 deletions spec/shoes/element_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
require_relative 'spec_helper'
require "shoes/color"
require 'shoes/element_methods'
require 'shoes/configuration'
require 'shoes/spec_helper'

describe "Basic Element Methods" do
class ElementMethodsShoeLaces
Expand Down Expand Up @@ -30,8 +27,9 @@ def initialize
it "should use self, gui_container, opts, blk" do
subject = ElementMethodsShoeLaces.new
subject.gui_container = "gui_container"
blk = lambda {}
blk = Proc.new {}
opts = mock(:hash)
opts.should_receive(:merge!).and_return(opts)
Shoes::Flow.should_receive(:new).
with(subject, "gui_container", opts, blk)
subject.flow opts, &blk
Expand Down
1 change: 1 addition & 0 deletions spec/shoes/line_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
describe "basic" do
subject { Shoes::Line.new(20, 23, 300, 430) }
it_behaves_like "object with stroke"
it_behaves_like "movable object"
end

shared_examples_for "basic line" do
Expand Down
1 change: 1 addition & 0 deletions spec/shoes/oval_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
subject { Shoes::Oval.new(20, 30, 100, 200) }
it_behaves_like "object with stroke"
it_behaves_like "object with fill"
it_behaves_like "movable object"
end

context "(eccentric)" do
Expand Down
3 changes: 3 additions & 0 deletions spec/shoes/shape_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
describe Shoes::Shape do
it_behaves_like "object with stroke"
it_behaves_like "object with style"
it_behaves_like "movable object"

describe "octagon" do
let(:draw) {
Expand All @@ -24,5 +25,7 @@
its(:bottom) { should eq(340) }
its(:width) { should eq(240) }
its(:height) { should eq(240) }

it_behaves_like "movable object"
end
end
8 changes: 8 additions & 0 deletions spec/shoes/shared_examples/common_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
shared_examples_for "movable object" do
it "moves" do
subject.move(300, 200)
subject.left.should eq(300)
subject.top.should eq(200)
end
end

2 changes: 1 addition & 1 deletion spec/shoes/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require 'spec_helper'

shared_examples = File.join(File.dirname(__FILE__), 'shared_examples', '**/*.rb')
Dir[shared_examples].each { |f| puts "requiring #{f}"; require f }
Dir[shared_examples].each { |f| require f }
1 change: 0 additions & 1 deletion spec/swt_shoes/animation_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
require 'spec_helper'
require 'swt_shoes/spec_helper'

describe SwtShoes::Animation do
Expand Down
Loading

0 comments on commit 5008464

Please sign in to comment.