diff --git a/lib/shoes/animation.rb b/lib/shoes/animation.rb index 1a86a93ee..fa041aded 100644 --- a/lib/shoes/animation.rb +++ b/lib/shoes/animation.rb @@ -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 diff --git a/lib/shoes/app.rb b/lib/shoes/app.rb index ba6e9e8f2..4dff2e412 100644 --- a/lib/shoes/app.rb +++ b/lib/shoes/app.rb @@ -34,6 +34,7 @@ def initialize(opts={}, &blk) self.opts = opts + @app = self @style = DEFAULT_STYLE gui_init diff --git a/lib/shoes/button.rb b/lib/shoes/button.rb index 06e30cf58..ea6afb0de 100644 --- a/lib/shoes/button.rb +++ b/lib/shoes/button.rb @@ -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 diff --git a/lib/shoes/common_methods.rb b/lib/shoes/common_methods.rb index 28c9b9b01..09358e27c 100644 --- a/lib/shoes/common_methods.rb +++ b/lib/shoes/common_methods.rb @@ -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) diff --git a/lib/shoes/element_methods.rb b/lib/shoes/element_methods.rb index ba966c687..df1b47f2b 100644 --- a/lib/shoes/element_methods.rb +++ b/lib/shoes/element_methods.rb @@ -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 @@ -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 diff --git a/lib/shoes/flow.rb b/lib/shoes/flow.rb index e28cabb3e..c693963a9 100644 --- a/lib/shoes/flow.rb +++ b/lib/shoes/flow.rb @@ -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 @@ -29,4 +30,4 @@ def initialize(parent_container, parent_gui_container, opts={}, blk = nil) end end -end \ No newline at end of file +end diff --git a/lib/shoes/shape.rb b/lib/shoes/shape.rb index 954aa7823..bf56b2668 100644 --- a/lib/shoes/shape.rb +++ b/lib/shoes/shape.rb @@ -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 = [] @@ -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 @@ -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 diff --git a/lib/shoes/timer_base.rb b/lib/shoes/timer_base.rb index a06a9c217..bc2d29474 100644 --- a/lib/shoes/timer_base.rb +++ b/lib/shoes/timer_base.rb @@ -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 diff --git a/lib/swt_shoes/animation.rb b/lib/swt_shoes/animation.rb index 6fbe20999..e5258d1dc 100644 --- a/lib/swt_shoes/animation.rb +++ b/lib/swt_shoes/animation.rb @@ -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 diff --git a/lib/swt_shoes/button.rb b/lib/swt_shoes/button.rb index 4c4bb51d8..193599e66 100644 --- a/lib/swt_shoes/button.rb +++ b/lib/swt_shoes/button.rb @@ -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 diff --git a/lib/swt_shoes/flow.rb b/lib/swt_shoes/flow.rb index ddc4d8ec2..21b8679c4 100644 --- a/lib/swt_shoes/flow.rb +++ b/lib/swt_shoes/flow.rb @@ -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 diff --git a/lib/swt_shoes/shape.rb b/lib/swt_shoes/shape.rb index 476667959..d1aec8e76 100644 --- a/lib/swt_shoes/shape.rb +++ b/lib/swt_shoes/shape.rb @@ -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 @@ -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 diff --git a/samples/README b/samples/README index 35a51af6d..944008290 100644 --- a/samples/README +++ b/samples/README @@ -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 diff --git a/spec/shoes/button_spec.rb b/spec/shoes/button_spec.rb index f6e410d2f..72f34544c 100644 --- a/spec/shoes/button_spec.rb +++ b/spec/shoes/button_spec.rb @@ -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 diff --git a/spec/shoes/element_methods_spec.rb b/spec/shoes/element_methods_spec.rb index 1c7f64124..49a1382f6 100644 --- a/spec/shoes/element_methods_spec.rb +++ b/spec/shoes/element_methods_spec.rb @@ -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 @@ -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 diff --git a/spec/shoes/line_spec.rb b/spec/shoes/line_spec.rb index 648127d55..2fea51193 100644 --- a/spec/shoes/line_spec.rb +++ b/spec/shoes/line_spec.rb @@ -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 diff --git a/spec/shoes/oval_spec.rb b/spec/shoes/oval_spec.rb index e7736bde5..8f9c1809f 100644 --- a/spec/shoes/oval_spec.rb +++ b/spec/shoes/oval_spec.rb @@ -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 diff --git a/spec/shoes/shape_spec.rb b/spec/shoes/shape_spec.rb index 5b4a76ae9..cdbd697dd 100644 --- a/spec/shoes/shape_spec.rb +++ b/spec/shoes/shape_spec.rb @@ -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) { @@ -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 diff --git a/spec/shoes/shared_examples/common_methods_spec.rb b/spec/shoes/shared_examples/common_methods_spec.rb new file mode 100644 index 000000000..c71241a1e --- /dev/null +++ b/spec/shoes/shared_examples/common_methods_spec.rb @@ -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 + diff --git a/spec/shoes/spec_helper.rb b/spec/shoes/spec_helper.rb index 376d8662b..3ee6917ae 100644 --- a/spec/shoes/spec_helper.rb +++ b/spec/shoes/spec_helper.rb @@ -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 } diff --git a/spec/swt_shoes/animation_spec.rb b/spec/swt_shoes/animation_spec.rb index 024a8cf3d..1a4d6ce43 100644 --- a/spec/swt_shoes/animation_spec.rb +++ b/spec/swt_shoes/animation_spec.rb @@ -1,4 +1,3 @@ -require 'spec_helper' require 'swt_shoes/spec_helper' describe SwtShoes::Animation do diff --git a/spec/swt_shoes/button_spec.rb b/spec/swt_shoes/button_spec.rb index a7e1a05c0..73f1bf5bf 100644 --- a/spec/swt_shoes/button_spec.rb +++ b/spec/swt_shoes/button_spec.rb @@ -6,17 +6,30 @@ #it_should_behave_like "A Common Element" - class ButtonShoeLaces - include SwtShoes::Button attr_accessor :gui_container, :gui_element, :text, :height, :width, :margin, :click_event_lambda + attr_accessor :app + + # because SwtShoes::Button#move calls super :( + def move(left, top) + # no-op + end end - let(:stub_gui_parent) { Swt.display } + let(:gui_container) { double("gui container", :get_layout => true) } + let(:gui_element) { double("gui element") } + let(:app_gui_container) { double("app gui container") } + let(:app) { double("app", :gui_container => app_gui_container) } let(:shoelace) { shoelace = ButtonShoeLaces.new - debugger - shoelace.parent_gui_container = stub_gui_parent + shoelace.extend described_class + shoelace.gui_container = gui_container + shoelace.gui_element = gui_element + shoelace.app = app shoelace } + + subject { shoelace } + + it_behaves_like "movable object with disposable gui element" end diff --git a/spec/swt_shoes/flow_spec.rb b/spec/swt_shoes/flow_spec.rb index 4ce6a7918..a937d81e8 100644 --- a/spec/swt_shoes/flow_spec.rb +++ b/spec/swt_shoes/flow_spec.rb @@ -1,5 +1,3 @@ -require "spec_helper" - require 'swt_shoes/spec_helper' describe SwtShoes::Flow do diff --git a/spec/swt_shoes/spec_helper.rb b/spec/swt_shoes/spec_helper.rb index 628948c16..a32a472ff 100644 --- a/spec/swt_shoes/spec_helper.rb +++ b/spec/swt_shoes/spec_helper.rb @@ -3,4 +3,4 @@ Shoes.configuration.framework = 'swt_shoes' shared_examples = File.join(File.expand_path(File.dirname(__FILE__)), 'shared_examples', '**/*.rb') -Dir[shared_examples].each { |f| puts "requiring #{f}"; require f } +Dir[shared_examples].each { |f| require f }