HowTo : Scrolling - mcorino/wxRuby3 GitHub Wiki

In many cases, the functionality offered by the available widgets in combination with available sizers provides everything needed to create great looking window panes and dialogs perfectly fitting available display areas.
However, it is also not uncommon to encounter situations where the available space on the display is just not enough to
get every bit of information crammed into widgets organized into the window available.
Luckily, wxRuby provides multiple options to work around such boundary situations. Widgets can be distributed over several
pages of a wizard dialog or multiple pages of a notebook control.
Another option is to use a scrolling container window which can have a (virtual) content size larger than the actually
visible viewport.
wxRuby provides three major scrolling widget base classes:
- Wx::ScrolledWindow provides a Wx::Panel like container window into which other widgets can be placed. Like Wx::Panel it handles TAB traversal and focus changes for child widgets.
- Wx::ScrolledControl is a base for scrolling widgets (typically not containing child widgets although aggregation to combine into new functionality is possible) that need themselves to process user input and/or display one or more items of data.
- Wx::ScrolledCanvas is a base for scrolling widgets without special consideration for child widgets of input processing and best suited for something like a drawing pane.
Note: A good (and extensive) example of the use of Wx::ScrolledCanvas can be found in the wxRuby-Shapes framework.
Below is a simple example of using Wx::ScrolledWindow to create a scrolling window pane containing (way) more widgets than visible and providing a view into the content that can be scrolled.
require 'wx'
class MyWindow < Wx::Frame
def initialize(title)
super(nil, title: title)
@scrolling_pane = Wx::ScrolledWindow.new(self, Wx::ID_ANY)
# the sizer will take care of determining the needed scroll size
# (if you don't use sizers you will need to manually set the viewport size)
sizer = Wx::BoxSizer.new(Wx::VERTICAL) # or Wx::VBoxSizer.new
# add a (large) series of widgets
120.times do |ix|
button = Wx::Button.new(@scrolling_pane, label: "Button #{ix+1}")
sizer.add(button, flag: Wx::ALL, border: 3)
end
@scrolling_pane.sizer = sizer
# this part makes the scrollbars show up
@scrolling_pane.fit_inside # ask the sizer about the needed size
@scrolling_pane.set_scroll_rate(5, 5)
centre
end
end
Wx::App.run do
window = MyWindow.new("wxRuby Scrolling Guide")
window.show
end
The output:
Below is a simple example of a simple scrolling widget displaying an image larger than the viewing area using Wx::ScrolledCanvas.
This simple widget could just as easily be build based on Wx::ScrolledControl. Just try replacing Wx::ScrolledCanvas for Wx::ScrolledControl. For most (if not all) custom widgets however it is advised to derive from ScrolledCanvas instead of ScrolledControl. See the documentation on Wx::Control (the base for ScrolledControl) for more information.
require 'wx'
class ScrollingImage < Wx::ScrolledCanvas
def initialize(parent, image_path, id=Wx::ID_ANY)
super(parent, id)
image = Wx::Image.new(image_path)
unless image.ok?
Wx.message_box('there was an error loading the image')
return
end
w = image.width
h = image.height
# init scrolled area size, scrolling speed, etc.
set_scrollbars(1, 1, w, h, 0, 0)
@bitmap = Wx::Bitmap.new(image)
# set up paint handler
evt_paint :on_paint
end
def on_paint(_evt)
paint_buffered do |dc|
prepare_dc(dc)
# render the image - in a real app, if your scrolled area
# is somewhat big, you will want to draw only visible parts,
# not everything like below
dc.draw_bitmap(@bitmap, 0, 0, false)
end
end
end
class MyWindow < Wx::Frame
def initialize(title)
super(nil, title: title)
panel = Wx::Panel.new(self)
panel.set_extra_style(Wx::WS_EX_BLOCK_EVENTS)
sizer = Wx::HBoxSizer.new
scroll_image = ScrollingImage.new(panel, 'motyl.png')
sizer.add(scroll_image, 1, flag: Wx::ALL|Wx::EXPAND, border: 20)
panel.sizer = sizer
panel.layout
sizer.fit(panel)
centre
end
end
Wx::App.run do
window = MyWindow.new("wxRuby Scrolling Guide")
window.show
end
The output:
Note: To run this example you will need the image file too. Download this here.
In some cases, it can be necessary to have two scrolling widgets displayed of which the view needs to be synchronized when scrolling.
Below is a simple example of two synchronized scrolling image widgets.
require 'wx'
class ScrollingImage < Wx::ScrolledCanvas
def initialize(parent, image_path, id=Wx::ID_ANY)
super(parent, id)
image = Wx::Image.new(image_path)
unless image.ok?
Wx.message_box('there was an error loading the image')
return
end
w = image.width
h = image.height
# init scrolled area size, scrolling speed, etc.
set_scrollbars(1, 1, w, h, 0, 0)
@bitmap = Wx::Bitmap.new(image)
@peer = nil
# set up paint handler
evt_paint :on_paint
# set up scroll interception
evt_scrollwin_thumbtrack :on_scroll
evt_scrollwin_thumbrelease :on_scroll
evt_scrollwin_lineup :on_scroll
evt_scrollwin_linedown :on_scroll
end
attr_accessor :peer
def on_paint(_evt)
paint_buffered do |dc|
prepare_dc(dc)
# render the image - in a real app, if your scrolled area
# is somewhat big, you will want to draw only visible parts,
# not everything like below
dc.draw_bitmap(@bitmap, 0, 0, false)
end
end
def on_scroll(evt)
if @peer
@peer.scroll(scroll_pos(Wx::HORIZONTAL), scroll_pos(Wx::VERTICAL))
end
evt.skip # let the event go
end
end
class MyWindow < Wx::Frame
def initialize(title)
super(nil, title: title)
panel = Wx::Panel.new(self)
panel.set_extra_style(Wx::WS_EX_BLOCK_EVENTS)
sizer = Wx::HBoxSizer.new
scroll_image1 = ScrollingImage.new(panel, 'motyl.png')
scroll_image2 = ScrollingImage.new(panel, 'motyl.png')
scroll_image2.peer = scroll_image1
scroll_image1.peer = scroll_image2
sizer.add(scroll_image1, 1, flag: Wx::ALL|Wx::EXPAND, border: 10)
sizer.add(scroll_image2, 1, flag: Wx::ALL|Wx::EXPAND, border: 10)
panel.sizer = sizer
panel.layout
sizer.fit(panel)
centre
end
end
Wx::App.run do
window = MyWindow.new("wxRuby Scrolling Guide")
window.show
end
The output:
Note: To run this example you will need the image file too. Download this here.