1#!/usr/bin/env perl 2 3# this extension implements popup-menu functionality for urxvt. it works 4# together with the urxvt::popup class - "no user serviceable parts inside". 5 6sub refresh { 7 my ($self) = @_; 8 9 my $cmd = "\x1b[H"; 10 11 my $row = 1; 12 for my $item (@{ $self->{data}{item} }) { 13 my $rend = "normal"; 14 15 if ($row == $self->{hover}) { 16 $rend = $self->{press} ? "active" : "hover"; 17 } 18 19 $cmd .= "$item->{rend}{$rend}\x1b[K"; 20 $cmd .= $self->locale_encode ($item->{render}->($item)); 21 $cmd .= "\015\012"; 22 23 $row++; 24 } 25 26 $self->cmd_parse (substr $cmd, 0, -2); 27} 28 29sub on_motion_notify { 30 my ($self, $event) = @_; 31 32 delete $self->{hover}; 33 34 my ($row, $col) = ($event->{row}, $event->{col}); 35 if ($col >= 0 && $col < $self->ncol 36 && $row >= 0 && $row < @{ $self->{data}{item} }) { 37 $self->{hover} = $event->{row} + 1; 38 } 39 $self->refresh; 40 41 1 42} 43 44sub on_button_press { 45 my ($self, $event) = @_; 46 47 $self->{press}[$event->{button}] = 1; 48 $self->refresh; 49 50 1 51} 52 53sub on_button_release { 54 my ($self, $event) = @_; 55 56 $self->{press}[$event->{button}] = 0; 57 58 my ($row, $col) = ($event->{row}, $event->{col}); 59 if ($col >= 0 && $col < $self->ncol 60 && $row >= 0 && $row < @{ $self->{data}{item} }) { 61 my $item = $self->{data}{item}[$row]; 62 $item->{activate}->($event, $item); 63 } 64 65 $self->refresh; 66 67 if ($event->{button} == $self->{data}{event}{button}) { 68 $self->ungrab; 69 $self->destroy; 70 } 71 72 1 73} 74 75sub on_focus_out { 76 my ($self) = @_; 77 78 delete $self->{hover}; 79 $self->refresh; 80 81 () 82} 83 84sub on_init { 85 my ($self) = @_; 86 87 my $data = $self->{data} = $urxvt::popup::self; 88 89 $_->{width} = $self->strwidth ($_->{text}) 90 for @{ $data->{item} }; 91 92 $self->resource (title => "URxvt Popup Menu"); 93 $self->resource (name => "URxvt.popup"); 94 95 $self->resource ($_ => $data->{term}->resource ($_)) 96 for qw(font boldFont italicFont boldItalicFont color+0 color+1); 97 98 my $width = List::Util::max map $_->{width}, @{ $data->{item} }; 99 my $height = @{ $data->{item} }; 100 101 my $pos = ""; 102 103 if ($data->{event}) { 104 my $x = int List::Util::max 0, $data->{event}{x_root} - $width * $data->{term}->fwidth * 0.5; 105 my $y = int List::Util::max 0, $data->{event}{y_root} - $data->{term}->fheight * 0.5; 106 $pos = "+$x+$y"; 107 } 108 109 $self->resource (geometry => "${width}x${height}$pos"); 110 111 $self->{term}{urxvt_popup_init_done} = 1; 112 113 () 114} 115 116sub on_start { 117 my ($self) = @_; 118 119 $self->cmd_parse ("\x1b[?25l\x1b[?7l"); 120 $self->refresh; 121 122 # might fail, but try anyways 123 $self->grab ($self->{data}{event}{time}, 1) 124 and $self->allow_events_async; 125 126 on_button_press $self, $self->{data}{event} if $self->{data}{event}{button}; 127 128 () 129} 130 131sub on_map_notify { 132 my ($self, $event) = @_; 133 134 # should definitely not fail 135 $self->grab ($self->{data}{event}{time}, 1) 136 and $self->allow_events_async; 137} 138 139 140