Josef said:
Josef Wolf wrote:
- maintain original coordinates at all zoom factors. Thus even after
zoom operations, events as well as querying/moving/adding items are
done as if the canvas is at zoom factor 1.0. So zoom is handled
completely transparent to the user of the module..
Yep, tkar does that too--object coordinates are independent of zoom
level. TkCanvas coordinates are hidden in the abstraction.
I see zoom_by does the scaling, adjusts the scrollregion and updates the
view. IMHO, to keep coordinates independent from zoom level, you would need
to intercept query/movement/creation of the items. Without that
TkcRectangle.new(canvas, [100,100], [300, 200])
canvas.zoom_by(2.0)
TkcRectangle.new(canvas, [100,100], [300, 200])
would result in two rectangles with different sizes.
[ ... ]
I think a ruby port of the perl Tk::AbstractCanvas would be useful, but in
a different way from tkar. [ ... ]
So OK. I thought, although I'm a complete newbie to ruby, I'd try to roll
my own. At least, that would result in a good exercise. So I started by
stealing the basics for a scrolled canvas from
http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb/ruby/ruby-talk/122597?122482-123428
and applying this patch:
--- lib/scrolledcanvas.rb.orig
+++ lib/scrolledcanvas.rb
@@ -6,6 +6,8 @@
include TkComposite
def initialize_composite(keys={})
+ @zoom = 1.0 # need this for the zoom_by method
+
@h_scr = TkScrollbar.new(@frame)
@v_scr = TkScrollbar.new(@frame)
@@ -23,7 +25,7 @@
@v_scr.grid
row=>0, :column=>1, :sticky=>'ns')
delegate('DEFAULT', @canvas)
- delegate('background', @text, @h_scr, @v_scr)
+ delegate('background', @frame, @h_scr, @v_scr) # looked like a typo
delegate('activeforeground', @h_scr, @v_scr)
delegate('troughcolor', @h_scr, @v_scr)
delegate('repeatdelay', @h_scr, @v_scr)
Then, I copied the zoon_by, xview and yview methods from your tkar package
and commented the call to adjust_scrollregion to avoid access to the
uninitialized @bounds array.
So at this stage, I have a canvas that can be scrolled and zoomed. Fine.
But how do I override the methods to create the items? In Perl/Tk, that
would be easy, since item creation is done via canvas methods. But in
Ruby/Tk, items are created via their own classes (e.g. TkcLine.new(args)
or something). There don't seem to exist methods in the Canvas class to
create items, which could easily be overridden.
Any hints?
PS: here's the current state of affairs:
#!/usr/bin/env ruby
require 'tk'
class TkScrolledCanvas < TkCanvas
include TkComposite
def initialize_composite(keys={})
@zoom = 1.0
@h_scr = TkScrollbar.new(@frame)
@v_scr = TkScrollbar.new(@frame)
@canvas = TkCanvas.new(@frame)
@path = @canvas.path
@canvas.xscrollbar(@h_scr)
@canvas.yscrollbar(@v_scr)
TkGrid.rowconfigure(@frame, 0, :weight=>1, :minsize=>0)
TkGrid.columnconfigure(@frame, 0, :weight=>1, :minsize=>0)
@canvas.grid
row=>0, :column=>0, :sticky=>'news')
@h_scr.grid
row=>1, :column=>0, :sticky=>'ew')
@v_scr.grid
row=>0, :column=>1, :sticky=>'ns')
delegate('DEFAULT', @canvas)
delegate('background', @text, @h_scr, @v_scr)
delegate('activeforeground', @h_scr, @v_scr)
delegate('troughcolor', @h_scr, @v_scr)
delegate('repeatdelay', @h_scr, @v_scr)
delegate('repeatinterval', @h_scr, @v_scr)
delegate('borderwidth', @frame)
delegate('relief', @frame)
delegate_alias('canvasborderwidth', 'borderwidth', @canvas)
delegate_alias('canvasrelief', 'relief', @canvas)
delegate_alias('scrollbarborderwidth', 'borderwidth', @h_scr, @v_scr)
delegate_alias('scrollbarrelief', 'relief', @h_scr, @v_scr)
configure(keys) unless keys.empty?
end
def zoom_by zf
zf = Float(zf)
@zoom *= zf
vf = (1 - 1/zf) / 2
x0, x1 = xview ; xf = x0 + vf * (x1-x0)
y0, y1 = yview ; yf = y0 + vf * (y1-y0)
scale 'all', 0, 0, zf, zf
adjust_scrollregion
xview "moveto", xf
yview "moveto", yf
end
def adjust_scrollregion
# configure :scrollregion => @bounds.map {|u|u*@zoom}
## if all of canvas can be shown, hide the scroll bars
end
def xview(mode=nil, *args)
if mode and mode == "scroll" and @follow_xdelta
number, what = args
x_pre, = xview
r = super(mode, *args)
x_post, = xview
x0,y0,x1,y1 = @bounds
@follow_xdelta += (x_post - x_pre) * (x1-x0)
r
elsif not mode
super()
else
super(mode, *args)
end
end
def yview(mode=nil, *args)
if mode and mode == "scroll" and @follow_ydelta
number, what = args
y_pre, = yview
r = super(mode, *args)
y_post, = yview
x0,y0,x1,y1 = @bounds
@follow_ydelta += (y_post - y_pre) * (y1-y0)
r
elsif not mode
super()
else
super(mode, *args)
end
end
end
root = TkRoot.new { title "zoomcanvas" }
c = TkScrolledCanvas.new
scrollregion=>[0,0,500,400],
:relief=>"sunken").pack
expand=>1,:fill=>"both")
TkcRectangle.new(c, [100,100], [300, 200])
c.bind("1", proc{|e| TkcRectangle.new(c, [100,100], [300, 200]) })
root.bind("z") { c.zoom_by(1.5) }
root.bind("Z") { c.zoom_by(1/1.5) }
Tk.mainloop