mirror of
https://github.com/gryf/tabbedalt.git
synced 2026-03-26 13:03:32 +01:00
Compare commits
6 Commits
e92fd3736f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8da342b9b0 | |||
| a9eb5cee5d | |||
| 7e76d39402 | |||
| 697a698add | |||
| 468c2f72f6 | |||
| 67c4f801ec |
72
README.rst
72
README.rst
@@ -8,9 +8,9 @@ terminal emulator
|
||||
Features
|
||||
--------
|
||||
|
||||
* Possibility to add named tabs, through the X resources, which represents its
|
||||
*class*. Without any configuration only default shell *class* is available
|
||||
under default (``Shift+Down``) shortcut.
|
||||
* Possibility to add named tabs, through the X resources, which run specified
|
||||
command or another shell (zsh/fish/ash/csh/ksh…). Without any configuration
|
||||
only default shell is available under default (``Shift+Down``) shortcut.
|
||||
|
||||
.. image:: /screens/tabbed.png
|
||||
:alt: Named tabs
|
||||
@@ -31,6 +31,8 @@ Features
|
||||
option. See below for examples.
|
||||
* Autohide tab when there is only one tab at the moment.
|
||||
* Ability to assign hotkey to jump to last tab.
|
||||
* Configurable confirm dialog for closing urxvt window, when there is more than
|
||||
one tab opened or there is an process still running.
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -119,6 +121,10 @@ You can change those values but bear in mind, that first group should have
|
||||
timeout in seconds set higher, than middle one. You can also change symbols for
|
||||
those groups.
|
||||
|
||||
Tab activity can be disabled by setting::
|
||||
|
||||
URxvt.tabbedalt.disable-activity: true
|
||||
|
||||
Flickering
|
||||
~~~~~~~~~~
|
||||
|
||||
@@ -247,8 +253,51 @@ still a way for selecting 10th tab, i.e.::
|
||||
In the example above, there are mapping for jump to tabs 1 - 12 using function
|
||||
keys, and `Control+0` to jump whatever last tab is.
|
||||
|
||||
Creating custom classes
|
||||
-----------------------
|
||||
Confirm closing window
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When working with tabs, sometimes user accidentally could close the window, and
|
||||
loose all the applications run on the tabs. There might be multiple tabs open,
|
||||
or just one with running process on it (i.e. some editor), where closing window
|
||||
by accident could result in data loss. To prevent this, there are two
|
||||
additional resources that can be set. First one, disabled by default is::
|
||||
|
||||
URxvt.tabbedalt.confirm-quit: false
|
||||
|
||||
When set to ``true`` it will either execute a message program or will display
|
||||
an urxvt overlay with the dialog directly on current tab. Note that overlay
|
||||
dialog will expect the user to either press:
|
||||
|
||||
- ``y`` or ``enter`` key to close the window
|
||||
- ``n`` or ``escape`` key to deny closing it
|
||||
|
||||
Second one is to provide X dialog program::
|
||||
|
||||
URxvt.tabbedalt.confirm-program:
|
||||
|
||||
It might be whatever X program, which can accept text as an argument, and can
|
||||
provide dialog which:
|
||||
|
||||
- have two buttons (i.e. yes/no, ok/cancel) where first will exit dialog with 0
|
||||
exit code and the latter will exit with whatever other number,
|
||||
- destroying the dialog also emit exit code higher than 0.
|
||||
|
||||
So, for example standard `xmessage`_ can be used::
|
||||
|
||||
URxvt.tabbedalt.confirm-program: xmessage -buttons ok:0,cancel:1
|
||||
|
||||
or `zenity`_::
|
||||
|
||||
URxvt.tabbedalt.confirm-program: zenity --question --title 'Close window' --text
|
||||
|
||||
or `kdialog`_::
|
||||
|
||||
URxvt.tabbedalt.confirm-program: kdialog --title 'Close window' --yesno
|
||||
|
||||
or… any other dialog programs which fulfill the above criteria.
|
||||
|
||||
Creating specific commands/shells
|
||||
---------------------------------
|
||||
|
||||
Let's assume, that one want to add three kind of custom shells:
|
||||
|
||||
@@ -259,14 +308,14 @@ Let's assume, that one want to add three kind of custom shells:
|
||||
A way to do this is to associate keystroke for it in ``.Xdefaults`` using
|
||||
urxvts ``keysym`` option, and the actions described above::
|
||||
|
||||
URxvt.keysym.Control-Shift-N: tabbedalt.new_tab:shell
|
||||
URxvt.keysym.Control-Shift-R: tabbedalt.new_tab:root:su -
|
||||
URxvt.keysym.Control-Shift-M: tabbedalt.new_tab:mc:mc
|
||||
URxvt.keysym.Control-Shift-N: tabbedalt:new_tab:shell
|
||||
URxvt.keysym.Control-Shift-R: tabbedalt:new_tab:root:su -
|
||||
URxvt.keysym.Control-Shift-M: tabbedalt:new_tab:mc:mc
|
||||
|
||||
Resource values are colon separated values, which are in order:
|
||||
|
||||
* **plugin name**, which in case of this very plugin would be always
|
||||
``tabbedalt``.
|
||||
* **plugin name:command**, which in this case of creating new tab will be
|
||||
``tabbedalt:new_tab``.
|
||||
* **title of the tab**, it could be anything but the colon.
|
||||
* **optional command**. If omitted, default shell will be launched.
|
||||
|
||||
@@ -281,3 +330,6 @@ feature was taken from `stepb`_ tabbedx repository.
|
||||
.. _activity indicator: http://mina86.com/2009/05/16/tabbed-urxvt-extension/
|
||||
.. _stepb: http://github.com/stepb/urxvt-tabbedex
|
||||
.. _tabbedex: https://github.com/mina86/urxvt-tabbedex
|
||||
.. _xmessage: https://gitlab.freedesktop.org/xorg/app/xmessage
|
||||
.. _zenity: https://wiki.gnome.org/Projects/Zenity
|
||||
.. _kdialog: https://develop.kde.org/docs/administration/kdialog
|
||||
|
||||
124
tabbedalt
124
tabbedalt
@@ -148,6 +148,20 @@
|
||||
#
|
||||
# 2022-09-18 14:26:00
|
||||
# - Add workaround for font flickering
|
||||
#
|
||||
# 2023-10-24 21:14:56
|
||||
# - Added confirmation when closing window when there is more than one tab or
|
||||
# there is a process still running.
|
||||
#
|
||||
# 2023-10-28 16:30:32
|
||||
# - Added overlay based dialog to confirm closing urxvt window
|
||||
#
|
||||
# 2024-06-10 10:34:47
|
||||
# - Added option for disabling activity marks and colors
|
||||
#
|
||||
# 2024-12-10 18:30:22
|
||||
# - Fix an issue with requesting closing terminal, which appear to be a
|
||||
# single instance
|
||||
|
||||
use Scalar::Util;
|
||||
|
||||
@@ -221,6 +235,7 @@ package urxvt::ext::tabbedalt::main;
|
||||
sub tab_activity_mark ($$) {
|
||||
my ($self, $tab) = @_;
|
||||
return ' ' unless defined $tab->{lastActivity};
|
||||
return ' ' if $self->{disable_activity};
|
||||
return ' ' if $tab == $self->{cur};
|
||||
if (defined $self->{timeouts}) {
|
||||
my $diff = int urxvt::NOW - $tab->{lastActivity};
|
||||
@@ -299,12 +314,14 @@ sub refresh {
|
||||
if ($tab == $self->{cur}) {
|
||||
@$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab}) x $len;
|
||||
} else {
|
||||
if ($act eq "*") {
|
||||
@$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_act}) x $len;
|
||||
} elsif ($act eq $self->{timeouts}[0][1]) {
|
||||
@$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acd}) x $len;
|
||||
} elsif ($act ne " ") {
|
||||
@$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acs}) x $len;
|
||||
if (!$self->{disable_activity}) {
|
||||
if ($act eq "*") {
|
||||
@$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_act}) x $len;
|
||||
} elsif ($act eq $self->{timeouts}[0][1]) {
|
||||
@$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acd}) x $len;
|
||||
} elsif ($act ne " ") {
|
||||
@$rend[$ofs .. $ofs + $len - 1] = ($self->{rs_tab_acs}) x $len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +366,7 @@ sub configure {
|
||||
# NOTE: switching between height +1 and height can be annoying,
|
||||
# especially well seen as flickering of part of the terminal on Intel
|
||||
# graphics and small fonts.
|
||||
|
||||
|
||||
# this is an extremely dirty way to force a configurenotify, but who
|
||||
# cares
|
||||
$tab->XMoveResizeWindow (
|
||||
@@ -467,12 +484,18 @@ sub _key_event {
|
||||
|
||||
_on focus_in => sub {
|
||||
my ($self, $event) = @_;
|
||||
if (defined($self->{is_being_destroyed})) {
|
||||
return
|
||||
}
|
||||
$self->{cur}->focus_in;
|
||||
()
|
||||
};
|
||||
|
||||
_on focus_out => sub {
|
||||
my ($self, $event) = @_;
|
||||
if (defined($self->{is_being_destroyed})) {
|
||||
return
|
||||
}
|
||||
$self->{cur}->focus_out;
|
||||
()
|
||||
};
|
||||
@@ -551,6 +574,9 @@ sub init {
|
||||
$self->{autohide} = $self->x_resource_boolean('autohide');
|
||||
$self->{zero_jump_last} = $self->x_resource_boolean('zero-jump-last');
|
||||
$self->{stop_flickering} = $self->x_resource_boolean('stop-flickering');
|
||||
$self->{confirm_quit} = $self->x_resource_boolean('confirm-quit');
|
||||
$self->{confirm_program} = $self->x_resource('confirm-program');
|
||||
$self->{disable_activity} = $self->x_resource_boolean('disable-activity');
|
||||
|
||||
my $timeouts = $self->x_resource ("tabbar-timeouts");
|
||||
$timeouts = '16:.:8:::4:+' unless defined $timeouts;
|
||||
@@ -576,6 +602,23 @@ sub init {
|
||||
|
||||
}
|
||||
|
||||
sub dialog_key_press {
|
||||
# react only on certain keys:
|
||||
# n and ESC - this case will delete the overlay
|
||||
# y and Enter - in this case, all tabs will be destroyed
|
||||
my ($tab, $event, $keysym, $str) = @_;
|
||||
|
||||
if ($keysym == 0xff0d || $keysym == 0xff8d || $keysym == 0x79) { # enter/y
|
||||
$tab->disable('key_press');
|
||||
$_->destroy for @{ $tab->{main}->{tabs} };
|
||||
} elsif ($keysym == 0xff1b || $keysym == 0x6e) { # escape/n
|
||||
delete $tab->{overlay};
|
||||
$tab->disable('key_press');
|
||||
$tab->{main}->refresh;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
_on start => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
@@ -614,7 +657,71 @@ _on configure_notify => sub {
|
||||
_on wm_delete_window => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
$_->destroy for @{ $self->{tabs} };
|
||||
if ($self->{confirm_quit}) {
|
||||
my $tab_count = @{ $self->{tabs} };
|
||||
my $subp_count = 0;
|
||||
my @subprocesses = split(/\n/, `ps --ppid $$ -o pid=,cmd=`);
|
||||
my %process_to_skip = ( "bash"=>1, "zsh"=>1, "ps"=>1, "fish"=>1, "sh"=>1 );
|
||||
|
||||
foreach my $line (@subprocesses) {
|
||||
$line =~ s/^\s+|\s+$//g;
|
||||
my @split_line = split(" ", $line);
|
||||
my $pid = @split_line[0];
|
||||
my @line = split(" ", @split_line[1]);
|
||||
my $proc_name = @line[0];
|
||||
$proc_name =~ s/^\s+|\s+$//g;
|
||||
if (!exists $process_to_skip{$proc_name}) {
|
||||
$subp_count = $subp_count + 1;
|
||||
} else {
|
||||
$subp_count = $subp_count + `ps --ppid $pid -o pid= | wc -l`;
|
||||
}
|
||||
}
|
||||
|
||||
if ($tab_count > 1 or $subp_count > 0) {
|
||||
my $msg = "";
|
||||
my $qst = "Are you sure you want to close urxvt?";
|
||||
$msg = "There are $tab_count tabs opened" if ($tab_count > 1);
|
||||
|
||||
if ($subp_count > 0) {
|
||||
if (! $msg) {
|
||||
$msg = "There is $subp_count active processes running.";
|
||||
} else {
|
||||
$msg = "$msg and there is $subp_count active processes running.";
|
||||
}
|
||||
}
|
||||
|
||||
if ($self->{confirm_program}) {
|
||||
`$self->{confirm_program} '$msg $qst'`;
|
||||
|
||||
if ($? == 0) {
|
||||
$self->{is_being_destroyed} = 1;
|
||||
# stop the timers, as we changed focus
|
||||
$self->{timer}->stop();
|
||||
$_->destroy for @{ $self->{tabs} };
|
||||
}
|
||||
} else {
|
||||
my $marginc = 0;
|
||||
$qst = "$qst (y/n)";
|
||||
if ((length $msg) > (length $qst)) {
|
||||
$marginc = int(($self->ncol - length $msg)/2);
|
||||
} elsif ((length $msg) <= (length $qst)) {
|
||||
$marginc = int(($self->ncol - length $qst)/2);
|
||||
}
|
||||
my $marginr = int(($self->nrow - 4)/2);
|
||||
my $tab = $self->{cur};
|
||||
|
||||
$tab->{overlay} = $tab->overlay_simple($marginc, $marginr,
|
||||
"$msg\n$qst");
|
||||
# action for removing all tabs will be decided in following
|
||||
# method.
|
||||
$tab->enable(key_press => \&dialog_key_press);
|
||||
}
|
||||
} else {
|
||||
$_->destroy for @{ $self->{tabs} };
|
||||
}
|
||||
} else {
|
||||
$_->destroy for @{ $self->{tabs} };
|
||||
}
|
||||
|
||||
1
|
||||
};
|
||||
@@ -821,7 +928,6 @@ sub start_rename_tab {
|
||||
1
|
||||
}
|
||||
|
||||
|
||||
sub _rename_tab_key_press {
|
||||
my ($tab, $event, $keysym, $str) = @_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user