#!/usr/bin/perl -w use strict; use Gtk2 -init; use Gtk2::Gdk::Keysyms; package Gtk2::CellRendererSpinButton; use POSIX qw(DBL_MAX UINT_MAX); use constant x_padding => 2; use constant y_padding => 3; use Gtk2::CellRenderer::Base; use Glib::Object::Subclass "Gtk2::CellRenderer::Base", signals => { edited => { flags => [qw(run-last)], param_types => [qw(Glib::String Glib::Double)], }, }, properties => [ Glib::ParamSpec -> boolean("editable", "Editable", "Can I change that?", 0, [qw(readable writable)]), Glib::ParamSpec -> uint("digits", "Digits", "How picky are you?", 0, UINT_MAX, 2, [qw(readable writable)]), map { Glib::ParamSpec -> double($_ -> [0], $_ -> [1], $_ -> [2], 0.0, DBL_MAX, $_ -> [3], [qw(readable writable)]) } (["value", "Value", "How much is the fish?", 0.0], ["min", "Min", "No way, I have to live!", 0.0], ["max", "Max", "Ah, you're too generous.", 100.0], ["step", "Step", "Okay.", 5.0]) ] ; sub calc_size { my ($cell, $layout) = @_; my ($width, $height) = $layout -> get_pixel_size(); return (0, 0, $width + x_padding * 2, $height + y_padding * 2); } sub on_get_size { my ($cell, $widget, $area) = @_; my $layout = $cell -> get_layout($widget); $layout -> set_text($cell -> get("value")); return $cell -> calc_size($layout); } sub get_layout { my ($cell, $widget) = @_; return $widget -> create_pango_layout(""); } sub on_render { my ($cell, $window, $widget, $background_area, $cell_area, $expose_area, $flags) = @_; my $state; if (grep {/selected/} @$flags) { $state = $widget -> has_focus() ? 'selected' : 'active'; } else { $state = $widget -> state() eq 'insensitive' ? 'insensitive' : 'normal'; } my $layout = $cell -> get_layout($widget); $layout -> set_text($cell -> get("value")); my ($x_offset, $y_offset, $width, $height) = $cell -> calc_size($layout); $widget -> get_style -> paint_layout($window, $state, 1, $cell_area, $widget, "cellrenderertext", $cell_area -> x() + $x_offset + x_padding, $cell_area -> y() + $y_offset + y_padding, $layout); } sub on_start_editing { my ($cell, $event, $view, $path, $background_area, $cell_area, $flags) = @_; my $spin_button = Gtk2::SpinButton -> new_with_range($cell -> get(qw(min max step))); $spin_button -> set_value($cell -> get("value")); $spin_button -> set_digits($cell -> get("digits")); $spin_button -> grab_focus(); $spin_button -> signal_connect(key_press_event => sub { my ($event_box, $event) = @_; if ($event -> keyval == $Gtk2::Gdk::Keysyms{ Return } || $event -> keyval == $Gtk2::Gdk::Keysyms{ KP_Enter }) { $spin_button -> update(); $cell -> signal_emit(edited => $path, $spin_button -> get_value()); $spin_button -> destroy(); return 1; } elsif ($event -> keyval == $Gtk2::Gdk::Keysyms{ Up }) { $spin_button -> spin('step-forward', ($spin_button -> get_increments())[0]); return 1; } elsif ($event -> keyval == $Gtk2::Gdk::Keysyms{ Down }) { $spin_button -> spin('step-backward', ($spin_button -> get_increments())[0]); return 1; } return 0; }); $spin_button -> show_all(); return $spin_button; } ############################################################################### package main; my $window = Gtk2::Window -> new("toplevel"); $window -> set_title ("CellRendererSpinButton"); $window -> signal_connect (delete_event => sub { Gtk2 -> main_quit(); }); my $model = Gtk2::ListStore -> new(qw(Glib::Double)); my $view = Gtk2::TreeView -> new($model); foreach (qw(12 12.1 12.12)) { $model -> set($model -> append(), 0 => $_); } my $renderer = Gtk2::CellRendererSpinButton -> new(); $renderer -> set(mode => "editable", min => 0, max => 1000, step => 2, digits => 2); $renderer -> signal_connect(edited => sub { my ($cell, $path, $new_value) = @_; $model -> set($model -> get_iter(Gtk2::TreePath -> new_from_string($path)), 0 => $new_value); }); my $column = Gtk2::TreeViewColumn -> new_with_attributes ("SpinButton", $renderer, value => 0); $view -> append_column($column); $window -> add($view); $window -> show_all(); Gtk2 -> main();