@matheusbsbs / matheus-bagliano / commits / d9c6469f38
Fork 0

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 }