| 1 |
package DC::UI::Dockbar; |
| 2 |
|
| 3 |
use common::sense; |
| 4 |
|
| 5 |
use DC::UI::Dockable; |
| 6 |
|
| 7 |
our @ISA = DC::UI::Toplevel::; |
| 8 |
|
| 9 |
sub new { |
| 10 |
my $class = shift; |
| 11 |
|
| 12 |
my $self = $class->SUPER::new ( |
| 13 |
border_bg => [1, 1, 1, 1], |
| 14 |
x => "max", |
| 15 |
y => 0, |
| 16 |
child => (my $nb = DC::UI::Notebook->new (expand => 1)), |
| 17 |
@_, |
| 18 |
); |
| 19 |
|
| 20 |
$self->{notebook} = $nb; |
| 21 |
|
| 22 |
$nb->connect (page_changed => sub { |
| 23 |
my ($nb, $page) = @_; |
| 24 |
$self->update_active ($page); |
| 25 |
0 |
| 26 |
}); |
| 27 |
|
| 28 |
$self |
| 29 |
} |
| 30 |
|
| 31 |
# This method is which you want to call to add a Dockable |
| 32 |
sub add_dock { |
| 33 |
my ($self, $dockable) = @_; |
| 34 |
$self->{docks}->{"$dockable"} = $dockable; |
| 35 |
delete $self->{dock_windows}->{"$dockable"}; |
| 36 |
|
| 37 |
$dockable->set_dockbar ($self); |
| 38 |
|
| 39 |
# TODO: capture the guards to remove these connections |
| 40 |
$dockable->connect (dock => sub { |
| 41 |
my ($dockable) = @_; |
| 42 |
#d# warn "DOCKABLE DOCK"; |
| 43 |
$self->dock ($dockable); |
| 44 |
0 |
| 45 |
}); |
| 46 |
|
| 47 |
$dockable->connect (undock => sub { |
| 48 |
my ($dockable) = @_; |
| 49 |
#d# warn "DOCKABLE UNDOCK"; |
| 50 |
$self->undock ($dockable); |
| 51 |
0 |
| 52 |
}); |
| 53 |
|
| 54 |
$dockable->connect (close_dock => sub { |
| 55 |
my ($dockable) = @_; |
| 56 |
$self->remove_dock ($dockable); |
| 57 |
0 |
| 58 |
}); |
| 59 |
|
| 60 |
$self->dock ($dockable); |
| 61 |
} |
| 62 |
|
| 63 |
# This method will remove the dockable from the Dockbar. Which means that the |
| 64 |
# window for this dockable is also removed (if it was undocked). |
| 65 |
sub remove_dock { |
| 66 |
my ($self, $dockable) = @_; |
| 67 |
|
| 68 |
$self->undock_window ($dockable); |
| 69 |
$self->undock_notebook ($dockable); |
| 70 |
delete $self->{docks}->{"$dockable"}; |
| 71 |
$dockable->set_dockbar (undef); |
| 72 |
} |
| 73 |
|
| 74 |
# This method makes sure the dockable is 'docked' into the Dockbar |
| 75 |
# and eg. removes the free floating window it maybe has. |
| 76 |
sub dock { |
| 77 |
my ($self, $dockable) = @_; |
| 78 |
$self->undock_window ($dockable); |
| 79 |
|
| 80 |
# here the assumption is done that $dockable is inserted at the end of the |
| 81 |
# notebook tabs, so that the other tabs dont have to be updated |
| 82 |
$self->{notebook}->add ($dockable); |
| 83 |
$dockable->set_dockbar_pos ($self->{notebook}->page_index ($dockable)); |
| 84 |
$self->update_active; |
| 85 |
} |
| 86 |
|
| 87 |
# (private) This method updates all docked tabs and tells them whether their |
| 88 |
# tab is 'active'. |
| 89 |
sub update_active { |
| 90 |
my ($self, $page) = @_; |
| 91 |
|
| 92 |
unless ($page) { |
| 93 |
$page = $self->{notebook}->get_current_page; |
| 94 |
} |
| 95 |
|
| 96 |
for ($self->{notebook}->pages) { |
| 97 |
$_->set_dockbar_tab_active ($_ eq $page); |
| 98 |
} |
| 99 |
} |
| 100 |
|
| 101 |
# This method undocks the dockable (if it isn't already undocked) and |
| 102 |
# creates a floating window for it. |
| 103 |
sub undock { |
| 104 |
my ($self, $dockable) = @_; |
| 105 |
return if $self->{dock_windows}->{"$dockable"}; |
| 106 |
|
| 107 |
$self->undock_notebook ($dockable); |
| 108 |
my $win = |
| 109 |
$self->{dock_windows}->{"$dockable"} = |
| 110 |
DC::UI::Toplevel->new ( |
| 111 |
title => $dockable->get_title, |
| 112 |
child => $dockable, |
| 113 |
force_w => 100, force_h => 100, |
| 114 |
x => 100, y => 100, |
| 115 |
has_close_button => 1, |
| 116 |
); |
| 117 |
|
| 118 |
$win->connect (delete => sub { |
| 119 |
$self->dock ($dockable); |
| 120 |
0 |
| 121 |
}); |
| 122 |
|
| 123 |
$win->show; |
| 124 |
} |
| 125 |
|
| 126 |
# (private) This method does the cleanup stuff when the dockable is docked |
| 127 |
# into the dockbar and had a floating window. |
| 128 |
sub undock_window { |
| 129 |
my ($self, $dockable) = @_; |
| 130 |
my $win = |
| 131 |
$self->{dock_windows}->{"$dockable"} |
| 132 |
or return; |
| 133 |
|
| 134 |
$win->remove ($dockable); |
| 135 |
delete $self->{dock_windows}->{"$dockable"}; |
| 136 |
$win->hide; # XXX: neccessary? |
| 137 |
} |
| 138 |
|
| 139 |
# (private) This method does the cleanup stuff which is neccessary when the |
| 140 |
# dockable is removed from the notebook. |
| 141 |
sub undock_notebook { |
| 142 |
my ($self, $dockable) = @_; |
| 143 |
$self->{notebook}->remove ($dockable); |
| 144 |
my $nextpage = ($self->{notebook}->pages)[0]; |
| 145 |
$self->{notebook}->set_current_page ($nextpage) |
| 146 |
if $nextpage; |
| 147 |
$dockable->set_dockbar_pos (undef); |
| 148 |
$dockable->set_dockbar_tab_active (undef); |
| 149 |
$self->update_dockbar_positions; |
| 150 |
} |
| 151 |
|
| 152 |
# (private) This method updates the position of the dockables in the dockbar. |
| 153 |
sub update_dockbar_positions { |
| 154 |
my ($self) = @_; |
| 155 |
my $i = 0; |
| 156 |
for ($self->{notebook}->pages) { |
| 157 |
$_->set_dockbar_pos ($i++); |
| 158 |
} |
| 159 |
} |
| 160 |
|
| 161 |
# Returns all Dockables of this Dockbar |
| 162 |
sub dockables { |
| 163 |
my ($self) = @_; |
| 164 |
values %{$self->{docks}} |
| 165 |
} |
| 166 |
|
| 167 |
# Returns whether the dockable is currently docked. (and not |
| 168 |
# a floating window). |
| 169 |
sub is_docked { |
| 170 |
my ($self, $dockable) = @_; |
| 171 |
return not exists $self->{dock_windows}->{"$dockable"}; |
| 172 |
} |
| 173 |
|
| 174 |
# switching to a page |
| 175 |
sub user_switch_to_page { |
| 176 |
my ($self, $page) = @_; |
| 177 |
$page = $page eq '0' ? 10 : $page; |
| 178 |
|
| 179 |
my @tabs = $self->{notebook}->pages; |
| 180 |
|
| 181 |
for (my $i = 0; $i < ($page - 1); $i++) { |
| 182 |
shift @tabs; |
| 183 |
} |
| 184 |
|
| 185 |
my $page = shift @tabs; |
| 186 |
return unless $page; |
| 187 |
|
| 188 |
$self->{notebook}->set_current_page ($page); |
| 189 |
} |
| 190 |
|
| 191 |
# This method activates the tab of the dockable if it is docked. |
| 192 |
sub select_dockable { |
| 193 |
my ($self, $dockable) = @_; |
| 194 |
return unless exists $self->{docks}->{"$dockable"}; |
| 195 |
$self->{notebook}->set_current_page ($dockable); |
| 196 |
} |
| 197 |
|
| 198 |
# close current tab |
| 199 |
sub close_current_tab { |
| 200 |
my ($self) = @_; |
| 201 |
|
| 202 |
if ($self->{notebook}->get_current_page) { |
| 203 |
my $curdock = $self->{notebook}->get_current_page; |
| 204 |
$curdock->close; |
| 205 |
} |
| 206 |
} |
| 207 |
|
| 208 |
# "activates" the current page |
| 209 |
sub activate_current { |
| 210 |
my ($self) = @_; |
| 211 |
|
| 212 |
if ($self->{notebook}->get_current_page) { |
| 213 |
$self->{notebook}->get_current_page->activate |
| 214 |
} |
| 215 |
} |
| 216 |
|
| 217 |
1 |