git-gui: update status bar to track operations
Update the status bar to track updates as individual "operations" that can overlap. Update all call sites to interact with the new status bar mechanism. Update initialization to explicitly clear status text, since otherwise it may persist across future operations. Signed-off-by: Jonathan Gilbert <JonathanG@iQmetrix.com> Signed-off-by: Pratyush Yadav <me@yadavpratyush.com>
Jonathan Gilbert committed
Dec 1, 2019 at 02:28 UTC
d9c6469f38737e9c96428a81adf0fc4d97606bb0
7 files changed
+354
-108
git-gui.sh
+15
-12
@@ -1797,10 +1797,10 @@ proc ui_status {msg} {
1797
}
1798
}
1799
1800
-proc ui_ready {{test {}}} {
1800
+proc ui_ready {} {
1801
global main_status
1802
if {[info exists main_status]} {
1803
- $main_status show [mc "Ready."] $test
1803
+ $main_status show [mc "Ready."]
1804
}
1805
}
1806
@@ -2150,8 +2150,6 @@ proc incr_font_size {font {amt 1}} {
2150
##
2151
## ui commands
2152
2153
-set starting_gitk_msg [mc "Starting gitk... please wait..."]
2154
-
2153
proc do_gitk {revs {is_submodule false}} {
2154
global current_diff_path file_states current_diff_side ui_index
2155
global _gitdir _gitworktree
@@ -2206,10 +2204,11 @@ proc do_gitk {revs {is_submodule false}} {
2204
set env(GIT_WORK_TREE) $_gitworktree
2205
cd $pwd
2206
2209
- ui_status $::starting_gitk_msg
2210
- after 10000 {
2211
- ui_ready $starting_gitk_msg
2212
- }
2207
+ set status_operation [$::main_status \
2208
+ start \
2209
+ [mc "Starting %s... please wait..." "gitk"]]
2210
+
2211
+ after 3500 [list $status_operation stop]
2212
}
2213
}
2214
@@ -2240,10 +2239,11 @@ proc do_git_gui {} {
2239
set env(GIT_WORK_TREE) $_gitworktree
2240
cd $pwd
2241
2243
- ui_status $::starting_gitk_msg
2244
- after 10000 {
2245
- ui_ready $starting_gitk_msg
2246
- }
2242
+ set status_operation [$::main_status \
2243
+ start \
2244
+ [mc "Starting %s... please wait..." "git-gui"]]
2245
+
2246
+ after 3500 [list $status_operation stop]
2247
}
2248
}
2249
@@ -4159,6 +4159,9 @@ if {$picked && [is_config_true gui.autoexplore]} {
4159
do_explore
4160
}
4161
4162
+# Clear "Initializing..." status
4163
+after 500 {$main_status show ""}
4164
+
4165
# Local variables:
4166
# mode: tcl
4167
# indent-tabs-mode: t
lib/blame.tcl
+17
-7
@@ -24,6 +24,7 @@ field w_cviewer ; # pane showing commit message
24
field finder ; # find mini-dialog frame
25
field gotoline ; # line goto mini-dialog frame
26
field status ; # status mega-widget instance
27
+field status_operation ; # operation displayed by status mega-widget
28
field old_height ; # last known height of $w.file_pane
29
30
@@ -274,6 +275,7 @@ constructor new {i_commit i_path i_jump} {
275
pack $w_cviewer -expand 1 -fill both
276
277
set status [::status_bar::new $w.status]
278
+ set status_operation {}
279
280
menu $w.ctxm -tearoff 0
281
$w.ctxm add command \
@@ -602,16 +604,23 @@ method _exec_blame {cur_w cur_d options cur_s} {
604
} else {
605
lappend options $commit
606
}
607
+
608
+ # We may recurse in from another call to _exec_blame and already have
609
+ # a status operation.
610
+ if {$status_operation == {}} {
611
+ set status_operation [$status start \
612
+ $cur_s \
613
+ [mc "lines annotated"]]
614
+ } else {
615
+ $status_operation restart $cur_s
616
+ }
617
+
618
lappend options -- $path
619
set fd [eval git_read --nice blame $options]
620
fconfigure $fd -blocking 0 -translation lf -encoding utf-8
621
fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
622
set current_fd $fd
623
set blame_lines 0
611
-
612
- $status start \
613
- $cur_s \
614
- [mc "lines annotated"]
624
}
625
626
method _read_blame {fd cur_w cur_d} {
@@ -806,10 +815,11 @@ method _read_blame {fd cur_w cur_d} {
815
[mc "Loading original location annotations..."]
816
} else {
817
set current_fd {}
809
- $status stop [mc "Annotation complete."]
818
+ $status_operation stop [mc "Annotation complete."]
819
+ set status_operation {}
820
}
821
} else {
812
- $status update $blame_lines $total_lines
822
+ $status_operation update $blame_lines $total_lines
823
}
824
} ifdeleted { catch {close $fd} }
825
@@ -1124,7 +1134,7 @@ method _blameparent {} {
1134
set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
1135
}
1136
if {[catch {set fd [eval git_read $diffcmd]} err]} {
1127
- $status stop [mc "Unable to display parent"]
1137
+ $status_operation stop [mc "Unable to display parent"]
1138
error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
1139
return
1140
}
lib/checkout_op.tcl
+8
-7
@@ -341,9 +341,9 @@ method _readtree {} {
341
global HEAD
342
343
set readtree_d {}
344
- $::main_status start \
344
+ set status_bar_operation [$::main_status start \
345
[mc "Updating working directory to '%s'..." [_name $this]] \
346
- [mc "files checked out"]
346
+ [mc "files checked out"]]
347
348
set fd [git_read --stderr read-tree \
349
-m \
@@ -354,26 +354,27 @@ method _readtree {} {
354
$new_hash \
355
]
356
fconfigure $fd -blocking 0 -translation binary
357
- fileevent $fd readable [cb _readtree_wait $fd]
357
+ fileevent $fd readable [cb _readtree_wait $fd $status_bar_operation]
358
}
359
360
-method _readtree_wait {fd} {
360
+method _readtree_wait {fd status_bar_operation} {
361
global current_branch
362
363
set buf [read $fd]
364
- $::main_status update_meter $buf
364
+ $status_bar_operation update_meter $buf
365
append readtree_d $buf
366
367
fconfigure $fd -blocking 1
368
if {![eof $fd]} {
369
fconfigure $fd -blocking 0
370
+ $status_bar_operation stop
371
return
372
}
373
374
if {[catch {close $fd}]} {
375
set err $readtree_d
376
regsub {^fatal: } $err {} err
376
- $::main_status stop [mc "Aborted checkout of '%s' (file level merging is required)." [_name $this]]
377
+ $status_bar_operation stop [mc "Aborted checkout of '%s' (file level merging is required)." [_name $this]]
378
warn_popup [strcat [mc "File level merge required."] "
379
380
$err
@@ -384,7 +385,7 @@ $err
385
return
386
}
387
387
- $::main_status stop
388
+ $status_bar_operation stop
389
_after_readtree $this
390
}
391
lib/choose_repository.tcl
+79
-41
@@ -9,6 +9,18 @@ field w_body ; # Widget holding the center content
9
field w_next ; # Next button
10
field w_quit ; # Quit button
11
field o_cons ; # Console object (if active)
12
+
13
+# Status mega-widget instance during _do_clone2 (used by _copy_files and
14
+# _link_files). Widget is destroyed before _do_clone2 calls
15
+# _do_clone_checkout
16
+field o_status
17
+
18
+# Operation displayed by status mega-widget during _do_clone_checkout =>
19
+# _readtree_wait => _postcheckout_wait => _do_clone_submodules =>
20
+# _do_validate_submodule_cloning. The status mega-widget is a different
21
+# instance than that stored in $o_status in earlier operations.
22
+field o_status_op
23
+
24
field w_types ; # List of type buttons in clone
25
field w_recentlist ; # Listbox containing recent repositories
26
field w_localpath ; # Entry widget bound to local_path
@@ -659,12 +671,12 @@ method _do_clone2 {} {
671
672
switch -exact -- $clone_type {
673
hardlink {
662
- set o_cons [status_bar::two_line $w_body]
674
+ set o_status [status_bar::two_line $w_body]
675
pack $w_body -fill x -padx 10 -pady 10
676
665
- $o_cons start \
677
+ set status_op [$o_status start \
678
[mc "Counting objects"] \
667
- [mc "buckets"]
679
+ [mc "buckets"]]
680
update
681
682
if {[file exists [file join $objdir info alternates]]} {
@@ -689,6 +701,7 @@ method _do_clone2 {} {
701
} err]} {
702
catch {cd $pwd}
703
_clone_failed $this [mc "Unable to copy objects/info/alternates: %s" $err]
704
+ $status_op stop
705
return
706
}
707
}
@@ -700,7 +713,7 @@ method _do_clone2 {} {
713
-directory [file join $objdir] ??]
714
set bcnt [expr {[llength $buckets] + 2}]
715
set bcur 1
703
- $o_cons update $bcur $bcnt
716
+ $status_op update $bcur $bcnt
717
update
718
719
file mkdir [file join .git objects pack]
@@ -708,7 +721,7 @@ method _do_clone2 {} {
721
-directory [file join $objdir pack] *] {
722
lappend tolink [file join pack $i]
723
}
711
- $o_cons update [incr bcur] $bcnt
724
+ $status_op update [incr bcur] $bcnt
725
update
726
727
foreach i $buckets {
@@ -717,10 +730,10 @@ method _do_clone2 {} {
730
-directory [file join $objdir $i] *] {
731
lappend tolink [file join $i $j]
732
}
720
- $o_cons update [incr bcur] $bcnt
733
+ $status_op update [incr bcur] $bcnt
734
update
735
}
723
- $o_cons stop
736
+ $status_op stop
737
738
if {$tolink eq {}} {
739
info_popup [strcat \
@@ -747,6 +760,8 @@ method _do_clone2 {} {
760
if {!$i} return
761
762
destroy $w_body
763
+
764
+ set o_status {}
765
}
766
full {
767
set o_cons [console::embed \
@@ -781,9 +796,9 @@ method _do_clone2 {} {
796
}
797
798
method _copy_files {objdir tocopy} {
784
- $o_cons start \
799
+ set status_op [$o_status start \
800
[mc "Copying objects"] \
786
- [mc "KiB"]
801
+ [mc "KiB"]]
802
set tot 0
803
set cmp 0
804
foreach p $tocopy {
@@ -798,7 +813,7 @@ method _copy_files {objdir tocopy} {
813
814
while {![eof $f_in]} {
815
incr cmp [fcopy $f_in $f_cp -size 16384]
801
- $o_cons update \
816
+ $status_op update \
817
[expr {$cmp / 1024}] \
818
[expr {$tot / 1024}]
819
update
@@ -808,17 +823,19 @@ method _copy_files {objdir tocopy} {
823
close $f_cp
824
} err]} {
825
_clone_failed $this [mc "Unable to copy object: %s" $err]
826
+ $status_op stop
827
return 0
828
}
829
}
830
+ $status_op stop
831
return 1
832
}
833
834
method _link_files {objdir tolink} {
835
set total [llength $tolink]
819
- $o_cons start \
836
+ set status_op [$o_status start \
837
[mc "Linking objects"] \
821
- [mc "objects"]
838
+ [mc "objects"]]
839
for {set i 0} {$i < $total} {} {
840
set p [lindex $tolink $i]
841
if {[catch {
@@ -827,15 +844,17 @@ method _link_files {objdir tolink} {
844
[file join $objdir $p]
845
} err]} {
846
_clone_failed $this [mc "Unable to hardlink object: %s" $err]
847
+ $status_op stop
848
return 0
849
}
850
851
incr i
852
if {$i % 5 == 0} {
835
- $o_cons update $i $total
853
+ $status_op update $i $total
854
update
855
}
856
}
857
+ $status_op stop
858
return 1
859
}
860
@@ -958,11 +977,26 @@ method _do_clone_checkout {HEAD} {
977
return
978
}
979
961
- set o_cons [status_bar::two_line $w_body]
980
+ set status [status_bar::two_line $w_body]
981
pack $w_body -fill x -padx 10 -pady 10
963
- $o_cons start \
982
+
983
+ # We start the status operation here.
984
+ #
985
+ # This function calls _readtree_wait as a callback.
986
+ #
987
+ # _readtree_wait in turn either calls _do_clone_submodules directly,
988
+ # or calls _postcheckout_wait as a callback which then calls
989
+ # _do_clone_submodules.
990
+ #
991
+ # _do_clone_submodules calls _do_validate_submodule_cloning.
992
+ #
993
+ # _do_validate_submodule_cloning stops the status operation.
994
+ #
995
+ # There are no other calls into this chain from other code.
996
+
997
+ set o_status_op [$status start \
998
[mc "Creating working directory"] \
965
- [mc "files"]
999
+ [mc "files"]]
1000
1001
set readtree_err {}
1002
set fd [git_read --stderr read-tree \
@@ -976,33 +1010,9 @@ method _do_clone_checkout {HEAD} {
1010
fileevent $fd readable [cb _readtree_wait $fd]
1011
}
1012
979
-method _do_validate_submodule_cloning {ok} {
980
- if {$ok} {
981
- $o_cons done $ok
982
- set done 1
983
- } else {
984
- _clone_failed $this [mc "Cannot clone submodules."]
985
- }
986
-}
987
-
988
-method _do_clone_submodules {} {
989
- if {$recursive eq {true}} {
990
- destroy $w_body
991
- set o_cons [console::embed \
992
- $w_body \
993
- [mc "Cloning submodules"]]
994
- pack $w_body -fill both -expand 1 -padx 10
995
- $o_cons exec \
996
- [list git submodule update --init --recursive] \
997
- [cb _do_validate_submodule_cloning]
998
- } else {
999
- set done 1
1000
- }
1001
-}
1002
-
1013
method _readtree_wait {fd} {
1014
set buf [read $fd]
1005
- $o_cons update_meter $buf
1015
+ $o_status_op update_meter $buf
1016
append readtree_err $buf
1017
1018
fconfigure $fd -blocking 1
@@ -1050,6 +1060,34 @@ method _postcheckout_wait {fd_ph} {
1060
fconfigure $fd_ph -blocking 0
1061
}
1062
1063
+method _do_clone_submodules {} {
1064
+ if {$recursive eq {true}} {
1065
+ $o_status_op stop
1066
+ set o_status_op {}
1067
+
1068
+ destroy $w_body
1069
+
1070
+ set o_cons [console::embed \
1071
+ $w_body \
1072
+ [mc "Cloning submodules"]]
1073
+ pack $w_body -fill both -expand 1 -padx 10
1074
+ $o_cons exec \
1075
+ [list git submodule update --init --recursive] \
1076
+ [cb _do_validate_submodule_cloning]
1077
+ } else {
1078
+ set done 1
1079
+ }
1080
+}
1081
+
1082
+method _do_validate_submodule_cloning {ok} {
1083
+ if {$ok} {
1084
+ $o_cons done $ok
1085
+ set done 1
1086
+ } else {
1087
+ _clone_failed $this [mc "Cannot clone submodules."]
1088
+ }
1089
+}
1090
+
1091
######################################################################
1092
##
1093
## Open Existing Repository
lib/index.tcl
+20
-11
@@ -45,13 +45,13 @@ proc _close_updateindex {fd after} {
45
wm deiconify $w
46
tkwait window $w
47
48
- $::main_status stop
48
+ $::main_status stop_all
49
unlock_index
50
rescan $after 0
51
return
52
}
53
54
- $::main_status stop
54
+ $::main_status stop_all
55
unlock_index
56
uplevel #0 $after
57
}
@@ -67,7 +67,7 @@ proc update_indexinfo {msg path_list after} {
67
set batch [expr {int($total_cnt * .01) + 1}]
68
if {$batch > 25} {set batch 25}
69
70
- $::main_status start $msg [mc "files"]
70
+ set status_bar_operation [$::main_status start $msg [mc "files"]]
71
set fd [git_write update-index -z --index-info]
72
fconfigure $fd \
73
-blocking 0 \
@@ -81,15 +81,18 @@ proc update_indexinfo {msg path_list after} {
81
$path_list \
82
$total_cnt \
83
$batch \
84
+ $status_bar_operation \
85
$after \
86
]
87
}
88
88
-proc write_update_indexinfo {fd path_list total_cnt batch after} {
89
+proc write_update_indexinfo {fd path_list total_cnt batch status_bar_operation \
90
+ after} {
91
global update_index_cp
92
global file_states current_diff_path
93
94
if {$update_index_cp >= $total_cnt} {
95
+ $status_bar_operation stop
96
_close_updateindex $fd $after
97
return
98
}
@@ -119,7 +122,7 @@ proc write_update_indexinfo {fd path_list total_cnt batch after} {
122
display_file $path $new
123
}
124
122
- $::main_status update $update_index_cp $total_cnt
125
+ $status_bar_operation update $update_index_cp $total_cnt
126
}
127
128
proc update_index {msg path_list after} {
@@ -133,7 +136,7 @@ proc update_index {msg path_list after} {
136
set batch [expr {int($total_cnt * .01) + 1}]
137
if {$batch > 25} {set batch 25}
138
136
- $::main_status start $msg [mc "files"]
139
+ set status_bar_operation [$::main_status start $msg [mc "files"]]
140
set fd [git_write update-index --add --remove -z --stdin]
141
fconfigure $fd \
142
-blocking 0 \
@@ -147,15 +150,18 @@ proc update_index {msg path_list after} {
150
$path_list \
151
$total_cnt \
152
$batch \
153
+ $status_bar_operation \
154
$after \
155
]
156
}
157
154
-proc write_update_index {fd path_list total_cnt batch after} {
158
+proc write_update_index {fd path_list total_cnt batch status_bar_operation \
159
+ after} {
160
global update_index_cp
161
global file_states current_diff_path
162
163
if {$update_index_cp >= $total_cnt} {
164
+ $status_bar_operation stop
165
_close_updateindex $fd $after
166
return
167
}
@@ -190,7 +196,7 @@ proc write_update_index {fd path_list total_cnt batch after} {
196
display_file $path $new
197
}
198
193
- $::main_status update $update_index_cp $total_cnt
199
+ $status_bar_operation update $update_index_cp $total_cnt
200
}
201
202
proc checkout_index {msg path_list after} {
@@ -204,7 +210,7 @@ proc checkout_index {msg path_list after} {
210
set batch [expr {int($total_cnt * .01) + 1}]
211
if {$batch > 25} {set batch 25}
212
207
- $::main_status start $msg [mc "files"]
213
+ set status_bar_operation [$::main_status start $msg [mc "files"]]
214
set fd [git_write checkout-index \
215
--index \
216
--quiet \
@@ -224,15 +230,18 @@ proc checkout_index {msg path_list after} {
230
$path_list \
231
$total_cnt \
232
$batch \
233
+ $status_bar_operation \
234
$after \
235
]
236
}
237
231
-proc write_checkout_index {fd path_list total_cnt batch after} {
238
+proc write_checkout_index {fd path_list total_cnt batch status_bar_operation \
239
+ after} {
240
global update_index_cp
241
global file_states current_diff_path
242
243
if {$update_index_cp >= $total_cnt} {
244
+ $status_bar_operation stop
245
_close_updateindex $fd $after
246
return
247
}
@@ -253,7 +262,7 @@ proc write_checkout_index {fd path_list total_cnt batch after} {
262
}
263
}
264
256
- $::main_status update $update_index_cp $total_cnt
265
+ $status_bar_operation update $update_index_cp $total_cnt
266
}
267
268
proc unstage_helper {txt paths} {
lib/merge.tcl
+9
-5
@@ -241,23 +241,27 @@ Continue with resetting the current changes?"]
241
if {[ask_popup $op_question] eq {yes}} {
242
set fd [git_read --stderr read-tree --reset -u -v HEAD]
243
fconfigure $fd -blocking 0 -translation binary
244
- fileevent $fd readable [namespace code [list _reset_wait $fd]]
245
- $::main_status start [mc "Aborting"] [mc "files reset"]
244
+ set status_bar_operation [$::main_status \
245
+ start \
246
+ [mc "Aborting"] \
247
+ [mc "files reset"]
248
+ fileevent $fd readable [namespace code [list \
249
+ _reset_wait $fd $status_bar_operation]]
250
} else {
251
unlock_index
252
}
253
}
254
251
-proc _reset_wait {fd} {
255
+proc _reset_wait {fd status_bar_operation} {
256
global ui_comm
257
254
- $::main_status update_meter [read $fd]
258
+ $status_bar_operation update_meter [read $fd]
259
260
fconfigure $fd -blocking 1
261
if {[eof $fd]} {
262
set fail [catch {close $fd} err]
259
- $::main_status stop
263
unlock_index
264
+ $status_bar_operation stop
265
266
$ui_comm delete 0.0 end
267
$ui_comm edit modified false
lib/status_bar.tcl
+206
-25
@@ -1,16 +1,42 @@
1
# git-gui status bar mega-widget
2
# Copyright (C) 2007 Shawn Pearce
3
4
+# The status_bar class manages the entire status bar. It is possible for
5
+# multiple overlapping asynchronous operations to want to display status
6
+# simultaneously. Each one receives a status_bar_operation when it calls the
7
+# start method, and the status bar combines all active operations into the
8
+# line of text it displays. Most of the time, there will be at most one
9
+# ongoing operation.
10
+#
11
+# Note that the entire status bar can be either in single-line or two-line
12
+# mode, depending on the constructor. Multiple active operations are only
13
+# supported for single-line status bars.
14
+
15
class status_bar {
16
17
+field allow_multiple ; # configured at construction
18
+
19
field w ; # our own window path
20
field w_l ; # text widget we draw messages into
21
field w_c ; # canvas we draw a progress bar into
22
field c_pack ; # script to pack the canvas with
10
-field status {}; # single line of text we show
11
-field prefix {}; # text we format into status
12
-field units {}; # unit of progress
13
-field meter {}; # current core git progress meter (if active)
23
+
24
+field baseline_text ; # text to show if there are no operations
25
+field status_bar_text ; # combined text for all operations
26
+
27
+field operations ; # list of current ongoing operations
28
+
29
+# The status bar can display a progress bar, updated when consumers call the
30
+# update method on their status_bar_operation. When there are multiple
31
+# operations, the status bar shows the combined status of all operations.
32
+#
33
+# When an overlapping operation completes, the progress bar is going to
34
+# abruptly have one fewer operation in the calculation, causing a discontinuity.
35
+# Therefore, whenever an operation completes, if it is not the last operation,
36
+# this counter is increased, and the progress bar is calculated as though there
37
+# were still another operation at 100%. When the last operation completes, this
38
+# is reset to 0.
39
+field completed_operation_count
40
41
constructor new {path} {
42
global use_ttk NS
@@ -18,12 +44,19 @@ constructor new {path} {
44
set w_l $w.l
45
set w_c $w.c
46
47
+ # Standard single-line status bar: Permit overlapping operations
48
+ set allow_multiple 1
49
+
50
+ set baseline_text ""
51
+ set operations [list]
52
+ set completed_operation_count 0
53
+
54
${NS}::frame $w
55
if {!$use_ttk} {
56
$w configure -borderwidth 1 -relief sunken
57
}
58
${NS}::label $w_l \
26
- -textvariable @status \
59
+ -textvariable @status_bar_text \
60
-anchor w \
61
-justify left
62
pack $w_l -side left
@@ -44,9 +77,16 @@ constructor two_line {path} {
77
set w_l $w.l
78
set w_c $w.c
79
80
+ # Two-line status bar: Only one ongoing operation permitted.
81
+ set allow_multiple 0
82
+
83
+ set baseline_text ""
84
+ set operations [list]
85
+ set completed_operation_count 0
86
+
87
${NS}::frame $w
88
${NS}::label $w_l \
49
- -textvariable @status \
89
+ -textvariable @status_bar_text \
90
-anchor w \
91
-justify left
92
pack $w_l -anchor w -fill x
@@ -56,7 +96,7 @@ constructor two_line {path} {
96
return $this
97
}
98
59
-method start {msg uds} {
99
+method ensure_canvas {} {
100
if {[winfo exists $w_c]} {
101
$w_c coords bar 0 0 0 20
102
} else {
@@ -68,31 +108,170 @@ method start {msg uds} {
108
$w_c create rectangle 0 0 0 20 -tags bar -fill navy
109
eval $c_pack
110
}
111
+}
112
+
113
+method show {msg} {
114
+ $this ensure_canvas
115
+ set baseline_text $msg
116
+ $this refresh
117
+}
118
+
119
+method start {msg {uds {}}} {
120
+ set baseline_text ""
121
+
122
+ if {!$allow_multiple && [llength $operations]} {
123
+ return [lindex $operations 0]
124
+ }
125
+
126
+ $this ensure_canvas
127
+
128
+ set operation [status_bar_operation::new $this $msg $uds]
129
+
130
+ lappend operations $operation
131
+
132
+ $this refresh
133
+
134
+ return $operation
135
+}
136
+
137
+method refresh {} {
138
+ set new_text ""
139
+
140
+ set total [expr $completed_operation_count * 100]
141
+ set have $total
142
+
143
+ foreach operation $operations {
144
+ if {$new_text != ""} {
145
+ append new_text " / "
146
+ }
147
+
148
+ append new_text [$operation get_status]
149
+
150
+ set total [expr $total + 100]
151
+ set have [expr $have + [$operation get_progress]]
152
+ }
153
+
154
+ if {$new_text == ""} {
155
+ set new_text $baseline_text
156
+ }
157
+
158
+ set status_bar_text $new_text
159
+
160
+ if {[winfo exists $w_c]} {
161
+ set pixel_width 0
162
+ if {$have > 0} {
163
+ set pixel_width [expr {[winfo width $w_c] * $have / $total}]
164
+ }
165
+
166
+ $w_c coords bar 0 0 $pixel_width 20
167
+ }
168
+}
169
+
170
+method stop {operation stop_msg} {
171
+ set idx [lsearch $operations $operation]
172
+
173
+ if {$idx >= 0} {
174
+ set operations [lreplace $operations $idx $idx]
175
+ set completed_operation_count [expr \
176
+ $completed_operation_count + 1]
177
+
178
+ if {[llength $operations] == 0} {
179
+ set completed_operation_count 0
180
+
181
+ destroy $w_c
182
+ if {$stop_msg ne {}} {
183
+ set baseline_text $stop_msg
184
+ }
185
+ }
186
+
187
+ $this refresh
188
+ }
189
+}
190
+
191
+method stop_all {{stop_msg {}}} {
192
+ # This makes the operation's call to stop a no-op.
193
+ set operations_copy $operations
194
+ set operations [list]
195
+
196
+ foreach operation $operations_copy {
197
+ $operation stop
198
+ }
199
+
200
+ if {$stop_msg ne {}} {
201
+ set baseline_text $stop_msg
202
+ }
203
+
204
+ $this refresh
205
+}
206
+
207
+method _delete {current} {
208
+ if {$current eq $w} {
209
+ delete_this
210
+ }
211
+}
212
+
213
+}
214
+
215
+# The status_bar_operation class tracks a single consumer's ongoing status bar
216
+# activity, with the context that there are a few situations where multiple
217
+# overlapping asynchronous operations might want to display status information
218
+# simultaneously. Instances of status_bar_operation are created by calling
219
+# start on the status_bar, and when the caller is done with its stauts bar
220
+# operation, it calls stop on the operation.
221
+
222
+class status_bar_operation {
223
+
224
+field status_bar; # reference back to the status_bar that owns this object
225
+
226
+field is_active;
227
+
228
+field status {}; # single line of text we show
229
+field progress {}; # current progress (0 to 100)
230
+field prefix {}; # text we format into status
231
+field units {}; # unit of progress
232
+field meter {}; # current core git progress meter (if active)
233
+
234
+constructor new {owner msg uds} {
235
+ set status_bar $owner
236
237
set status $msg
238
+ set progress 0
239
set prefix $msg
240
set units $uds
241
set meter {}
242
+
243
+ set is_active 1
244
+
245
+ return $this
246
}
247
248
+method get_is_active {} { return $is_active }
249
+method get_status {} { return $status }
250
+method get_progress {} { return $progress }
251
+
252
method update {have total} {
79
- set pdone 0
80
- set cdone 0
253
+ if {!$is_active} { return }
254
+
255
+ set progress 0
256
+
257
if {$total > 0} {
82
- set pdone [expr {100 * $have / $total}]
83
- set cdone [expr {[winfo width $w_c] * $have / $total}]
258
+ set progress [expr {100 * $have / $total}]
259
}
260
261
set prec [string length [format %i $total]]
262
+
263
set status [mc "%s ... %*i of %*i %s (%3i%%)" \
264
$prefix \
265
$prec $have \
266
$prec $total \
91
- $units $pdone]
92
- $w_c coords bar 0 0 $cdone 20
267
+ $units $progress]
268
+
269
+ $status_bar refresh
270
}
271
272
method update_meter {buf} {
273
+ if {!$is_active} { return }
274
+
275
append meter $buf
276
set r [string last "\r" $meter]
277
if {$r == -1} {
@@ -109,23 +288,25 @@ method update_meter {buf} {
288
}
289
}
290
112
-method stop {{msg {}}} {
113
- destroy $w_c
114
- if {$msg ne {}} {
115
- set status $msg
291
+method stop {{stop_msg {}}} {
292
+ if {$is_active} {
293
+ set is_active 0
294
+ $status_bar stop $this $stop_msg
295
}
296
}
297
119
-method show {msg {test {}}} {
120
- if {$test eq {} || $status eq $test} {
121
- set status $msg
122
- }
298
+method restart {msg} {
299
+ if {!$is_active} { return }
300
+
301
+ set status $msg
302
+ set prefix $msg
303
+ set meter {}
304
+ $status_bar refresh
305
}
306
125
-method _delete {current} {
126
- if {$current eq $w} {
127
- delete_this
128
- }
307
+method _delete {} {
308
+ stop
309
+ delete_this
310
}
311
312
}