#! perl # this extension implements popup-menu functionality for urxvt. it works # together with the urxvt::popup class - "no user serviceable parts inside". sub refresh { my ($self) = @_; my $cmd = "\x1b[H"; my $row = 1; for my $item (@{ $self->{data}{item} }) { my $rend = "normal"; if ($row == $self->{hover}) { $rend = $self->{press} ? "active" : "hover"; } $cmd .= "$item->{rend}{$rend}\x1b[K"; $cmd .= $self->locale_encode ($item->{render}->($item)); $cmd .= "\015\012"; $row++; } $self->cmd_parse (substr $cmd, 0, -2); } sub on_motion_notify { my ($self, $event) = @_; delete $self->{hover}; my ($row, $col) = ($event->{row}, $event->{col}); if ($col >= 0 && $col < $self->ncol && $row >= 0 && $row < @{ $self->{data}{item} }) { $self->{hover} = $event->{row} + 1; } $self->refresh; 1 } sub on_button_press { my ($self, $event) = @_; $self->{press}[$event->{button}] = 1; $self->refresh; 1 } sub on_button_release { my ($self, $event) = @_; $self->{press}[$event->{button}] = 0; my ($row, $col) = ($event->{row}, $event->{col}); if ($col >= 0 && $col < $self->ncol && $row >= 0 && $row < @{ $self->{data}{item} }) { my $item = $self->{data}{item}[$row]; $item->{activate}->($event, $item); } $self->refresh; if ($event->{button} == $self->{data}{event}{button}) { $self->ungrab; $self->destroy; } 1 } sub on_focus_out { my ($self) = @_; delete $self->{hover}; $self->refresh; () } sub on_init { my ($self) = @_; my $data = $self->{data} = $urxvt::popup::self; $_->{width} = $self->strwidth ($_->{text}) for @{ $data->{item} }; $self->resource (title => "URxvt Popup Menu"); $self->resource (name => "URxvt.popup"); $self->resource ($_ => $data->{term}->resource ($_)) for qw(font boldFont italicFont boldItalicFont color+0 color+1); my $width = List::Util::max map $_->{width}, @{ $data->{item} }; my $height = @{ $data->{item} }; my $pos = ""; if ($data->{event}) { my $x = int List::Util::max 0, $data->{event}{x_root} - $width * $data->{term}->fwidth * 0.5; my $y = int List::Util::max 0, $data->{event}{y_root} - $data->{term}->fheight * 0.5; $pos = "+$x+$y"; } $self->resource (geometry => "${width}x${height}$pos"); $self->{term}{urxvt_popup_init_done} = 1; () } sub on_start { my ($self) = @_; $self->cmd_parse ("\x1b[?25l\x1b[?7l"); $self->refresh; # might fail, but try anyways $self->grab ($self->{data}{event}{time}, 1) and $self->allow_events_async; on_button_press $self, $self->{data}{event} if $self->{data}{event}{button}; () } sub on_map_notify { my ($self, $event) = @_; # should definitely not fail $self->grab ($self->{data}{event}{time}, 1) and $self->allow_events_async; }