Gtk2::Ex::TickerView -- scrolling ticker display widget
use Gtk2::Ex::TickerView; my $ticker = Gtk2::Ex::TickerView->new (model => $model); my $renderer = Gtk2::CellRendererText->new; $ticker->pack_start ($renderer, 0); $ticker->set_attributes ($renderer, text => 0); # column
Gtk2::Ex::TickerView is a subclass of
Gtk2::DrawingArea, but that might change so it's recommended you only rely on
Gtk2::Widget Gtk2::DrawingArea Gtk2::Ex::TickerView
The interfaces implemented are:
Gtk2::Buildable (Gtk 2.12 and up) Gtk2::CellLayout
orientation property is compatible with the Gtk2::Orientable interface, but that interface can't be added as of Perl-Gtk 1.222.
Gtk2::Ex::TickerView widget displays items from a
Gtk2::TreeModel scrolling across the window, like a news bar or stock ticker.
+----------------------------------------------------------+ | st item * The second item * The third item * The fou | +----------------------------------------------------------+ <---- scrolling
vertical orientation it scrolls upwards.
+-------+ | Two | ^ | Three | | | Four | | scrolling | Five | | | Six | | | ... | +-------+
Items are drawn with one or more
Gtk2::CellRenderer objects set into the TickerView as per the CellLayout interface (see Gtk2::CellLayout). For example to scroll text you can use
Gtk2::CellRendererText as a renderer.
Mouse button 1 is setup for the user to drag the display back and forwards. This is good to go back and see something that's just moved off the edge, or to skip past boring bits. Perhaps in the future the button used will be customizable.
Mouse wheel scrolling moves the display back and forwards by 10% of the window, or a page 90% if the control key is held down. An up/down scroll will act on a horizontal ticker too, advancing or reversing, which is handy for a mouse with only an up/down wheel. Similarly a left/right scroll on a vertical ticker. But this is a bit experimental and might change or become customizable.
If two or more renderers are set then they're drawn one after the other for each item, ie. row of the model. For example you could have a
Gtk2::CellRendererPixbuf to draw an icon then a
Gtk2::CellRendererText to draw some text and they scroll across together (or upwards above each other when vertical). The icon could use the row data, or just be a fixed image to go before every item.
+-----------------------------------------------+ | +--------++--------++--------++--------+ | | ...| Pixbuf || Text || Pixbuf || Text |...| | | row 35 || row 35 || row 36 || row 36 | | | +--------++--------++--------++--------+ | +-----------------------------------------------+
The display and scrolling direction follow the left-to-right or right-to-left of
set_direction (see Gtk2::Widget). For
ltr mode item 0 starts at the left of the window and items scroll off to the left. For
rtl item 0 starts at the right of the window and items scroll to the right.
+----------------------------------------------------------+ | m five * item four * item three * item two * item on | +----------------------------------------------------------+ rtl mode, scrolling ----->
In vertical mode
ltr scrolls upwards and
rtl scrolls downwards. This doesn't make as much sense as it does horizontally. (Perhaps it should change, though if you have to set vertical orientation it's not too terrible that
set_direction is the slightly unusual case of a downwards scroll.)
Within each renderer cell any text or drawing direction is a matter for that renderer. For example in
Gtk2::CellRendererText Pango recognises right-to-left scripts such as Arabic based on the characters and shouldn't need any special setups. (But if you want to rotate 90 degrees for something vertical it might be much trickier. Just setting text "gravity" doesn't work. See examples/vertical-rottext.pl for one way to do it.)
Currently only a list style model is expected, meaning only a single level, and only that topmost level of the model is drawn. For example a
Gtk2::ListStore suits. Perhaps in the future something will be done to descend into and draw subrows too.
The whole Gtk model/view/layout/renderer/attributes as used here is ridiculously complicated. Its power comes when showing a big updating list or wanting customized drawing, but the amount of code to get something on the screen is not nice. Have a look at "Tree and List Widget Overview" in the Gtk reference manual if you haven't already. Then examples/simple.pl is more or less the minimum to actually display something.
$ticker = Gtk2::Ex::TickerView->new (key => value, ...)
Create and return a new
Gtk2::Ex::TickerView widget. Optional key/value pairs set initial properties as per
Glib::Object->new (see Glib::Object).
Scroll the ticker contents across by
$n pixels. Postive
$n moves in the normal scrolled direction or negative goes backwards.
The display position is maintained as a floating point value so fractional
$n amounts accumulate until a whole pixel step is reached.
Scroll the ticker contents back to the start, ie. to show the first row of the model at the left end of the window (or upper end for vertical, or right end for
rtl, or bottom end for vertical plus
$path = $ticker->get_path_at_pos ($x, $y)
Gtk2::TreePath which is the model row displayed at
$y, or return
undef if there's nothing displayed there. There can be nothing if no
model is set, or it has no rows, or all rows are zero width or not visible (the renderer
$x can be outside the window, in which case the item which would be shown at that point is still returned.
$y is currently ignored, since all items simply use the full window height. Perhaps in the future a
$y outside the window height will cause an
Gtk2::TreeModel, default undef)
This is any
Glib::Object implementing the
Gtk2::TreeModel interface, for example a
Gtk2::ListStore. It supplies the data to be displayed. Until this is set the ticker is blank.
run(boolean, default true)
Whether to run the ticker, ie. to scroll it across under a timer. If false then the ticker just draws the items at its current position without moving (except by the programatic scroll functions above, or user dragging with mouse button 1).
speed(floating point pixels per second, default 25)
The speed the items scroll across, in pixels per second.
frame-rate(floating point frames per second, default 4)
The number of times each second the ticker moves and redraws. Each move will be
speed divided by
frame-rate many pixels.
The current current code uses the Glib main loop timer so the frame rate becomes integer milliseconds for actual use. A minimum 1 millisecond is imposed, meaning frame rates more than 1000 are treated as 1000. Of course 1000 frames a second is pointlessly high.
If set to
"vertical" the ticker items are drawn vertically from the top of the window downwards, and scroll up the screen. Or with
rtl mode the direction reverses so they're drawn from the bottom of the window upwards, and scroll down the screen.
rtl doesn't make a great deal of sense in vertical mode. Something to reverse the direction is certainly desired, but perhaps it shouldn't be the LtoR/RtoL setting ...)
fixed-height-mode(boolean, default false)
If true then assume all rows in the model have the same height and that it doesn't change. This allows the ticker to get its desired height by asking the renderers about just one row of the model, instead of going through them all and resizing on every insert, delete or change. If the model is big this is a significant speedup.
If you force a height with
set_size_request in the usual widget fashion then you should turn on
fixed-height-mode too because even with
set_size_request the sizing mechanism ends up running the widget size code even though it then overrides the result.
visible property in each cell renderer is recognised and a renderer that's not visible is skipped and takes no space.
visible can be set permanently in the renderer to suppress it entirely, or controlled with the attributes mechanism or data setup function to suppress have it just for selected rows of the model.
Suppressing lots of rows using
visible might be a bit slow since TickerView must setup the renderers for each row to see the state. A
Gtk2::TreeModelFilter may be a better way to pick out a small number of desired rows from a very big model.
TickerView implements the
Gtk2::Buildable interface of Gtk 2.12 and up, allowing
Gtk2::Builder to construct a ticker. The class name is
Gtk2__Ex__TickerView and renderers and attributes are added as children per
Gtk2::CellLayout. Here's a sample, or see examples/builder.pl for a complete program,
<object class="Gtk2__Ex__TickerView" id="myticker"> <property name="model">myliststore</property> <child> <object class="GtkCellRendererText" id="myrenderer"> <property name="xpad">10</property> </object> <attributes> <attribute name="text">0</attribute> </attributes> </child> </object>
But see "BUILDABLE INTERFACE" in Gtk2::Ex::CellLayout::Base for caveats about widget superclass tags (like the "accessibility" settings) which end up unavailable (as of Gtk2-Perl 1.222 at least).
The Gtk reference documentation for
GtkCellLayout doesn't really describe how
pack_end order the cells, but it's the same as
GtkBox and a description can be found there. Basically each cell is noted as "start" or "end", with "starts" drawn from the left and "ends" from the right (vice versa in RtoL mode). In a TickerView the ends immediately follow the starts, there's no gap in between, unlike say in a
Gtk2::HBox. (Which means the "expand" parameter is ignored currently.) See examples/order.pl for a demonstration.
When the model has no rows the TickerView's desired height from
size_request is zero. This is bad if you want a visible but blank area when there's nothing to display. But there's no way TickerView can work out a height when it's got no data at all to set into the renderers. You can try calculating a fixed height from a sample model and
set_size_request to force that, or alternately have a "no data" row displaying in the model instead of letting it go empty, or even switch to a dummy model with a "no data" row when the real one is empty.
Cells are drawn into an off-screen pixmap which is copied to the window at successively advancing X positions as the ticker scrolls across. The aim is to run the model fetching and cell rendering just once for each row as it appears on screen. This is important because the model+renderer mechanism is generally much too slow and bloated to call at frame-rate times per second.
The drawing for scroll movement goes through a SyncCall (see Gtk2::Ex::SyncCall) so that after drawing one frame the next doesn't go out until hearing back from the server that it finished the previous. This ensures a high frame rate doesn't flood the server with more drawing than it can keep up with, but instead dynamically caps at client+server capability.
Scroll movements are calculated from elapsed time using
clock_gettime(CLOCK_REALTIME) when available or high-res system time otherwise (see
Time::HiRes). This means the display moves at the
speed setting even if drawing is not keeping up with the requested
frame-rate. Slow frame rates can occur on the client side if the main loop is busy doing other things (or momentarily blocked completely), or can be on the X server side if it's busy with other drawing etc.
Gtk2::CellLayout, Gtk2::TreeModel, Gtk2::CellRenderer, Gtk2::Ex::CellLayout::Base
Copyright 2007, 2008, 2009, 2010, 2011, 2017, 2019 Kevin Ryde
Gtk2-Ex-TickerView is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
Gtk2-Ex-TickerView is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Gtk2-Ex-TickerView. If not, see http://www.gnu.org/licenses/.