Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show visual explosion effect when nuking worktree #2861

Merged
merged 4 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions demo/record_demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mkdir -p demo/output

terminalizer -c demo/config.yml record --skip-sharing -d "go run cmd/integration_test/main.go cli --slow $TEST" "demo/output/$NAME"
terminalizer render "demo/output/$NAME" -o "demo/output/$NAME.gif"
gifsicle --colors 256 --use-col=web -O3 < "demo/output/$NAME.gif" > "demo/output/$NAME-compressed.gif"
COMPRESSED_PATH="demo/output/$NAME-compressed.gif"
gifsicle --colors 256 --use-col=web -O3 < "demo/output/$NAME.gif" > "$COMPRESSED_PATH"

echo "Demo recorded to demo/$NAME-compressed.gif"
echo "Demo recorded to $COMPRESSED_PATH"
1 change: 1 addition & 0 deletions docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ gui:
splitDiff: 'auto' # one of 'auto' | 'always'
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
border: 'single' # one of 'single' | 'double' | 'rounded' | 'hidden'
animateExplosion: true # shows an explosion animation when nuking the working tree
git:
paging:
colorArg: always
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type GuiConfig struct {
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
WindowSize string `yaml:"windowSize"`
Border string `yaml:"border"`
AnimateExplosion bool `yaml:"animateExplosion"`
}

type ThemeConfig struct {
Expand Down Expand Up @@ -454,6 +455,7 @@ func GetDefaultConfig() *UserConfig {
SplitDiff: "auto",
SkipRewordInEditorWarning: false,
Border: "single",
AnimateExplosion: true,
},
Git: GitConfig{
Paging: PagingConfig{
Expand Down
108 changes: 108 additions & 0 deletions pkg/gui/controllers/workspace_reset_controller.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package controllers

import (
"bytes"
"fmt"
"math"
"math/rand"
"time"

"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
Expand All @@ -29,6 +34,10 @@ func (self *FilesController) createResetMenu() error {
return self.c.Error(err)
}

if self.c.UserConfig.Gui.AnimateExplosion {
self.animateExplosion()
}

return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
},
Key: 'x',
Expand Down Expand Up @@ -135,3 +144,102 @@ func (self *FilesController) createResetMenu() error {

return self.c.Menu(types.CreateMenuOptions{Title: "", Items: menuItems})
}

func (self *FilesController) animateExplosion() {
self.Explode(self.c.Views().Files, func() {
err := self.c.PostRefreshUpdate(self.c.Contexts().Files)
if err != nil {
self.c.Log.Error(err)
}
})
}

// Animates an explosion within the view by drawing a bunch of flamey characters
func (self *FilesController) Explode(v *gocui.View, onDone func()) {
width := v.InnerWidth()
height := v.InnerHeight() + 1
styles := []style.TextStyle{
style.FgLightWhite.SetBold(),
style.FgYellow.SetBold(),
style.FgRed.SetBold(),
style.FgBlue.SetBold(),
style.FgBlack.SetBold(),
}

self.c.OnWorker(func(_ gocui.Task) {
max := 25
for i := 0; i < max; i++ {
image := getExplodeImage(width, height, i, max)
style := styles[(i*len(styles)/max)%len(styles)]
coloredImage := style.Sprint(image)
self.c.OnUIThread(func() error {
v.SetContent(coloredImage)
return nil
})
time.Sleep(time.Millisecond * 20)
}
self.c.OnUIThread(func() error {
v.Clear()
onDone()
return nil
})
})
}

// Render an explosion in the given bounds.
func getExplodeImage(width int, height int, frame int, max int) string {
// Predefine the explosion symbols
explosionChars := []rune{'*', '.', '@', '#', '&', '+', '%'}

// Initialize a buffer to build our string
var buf bytes.Buffer

// Initialize RNG seed
rand.Seed(time.Now().UnixNano())

// calculate the center of explosion
centerX, centerY := width/2, height/2

// calculate the max radius (hypotenuse of the view)
maxRadius := math.Hypot(float64(centerX), float64(centerY))

// calculate frame as a proportion of max, apply square root to create the non-linear effect
progress := math.Sqrt(float64(frame) / float64(max))

// calculate radius of explosion according to frame and max
radius := progress * maxRadius * 2

// introduce a new radius for the inner boundary of the explosion (the shockwave effect)
var innerRadius float64
if progress > 0.5 {
innerRadius = (progress - 0.5) * 2 * maxRadius
}

for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
// calculate distance from center, scale x by 2 to compensate for character aspect ratio
distance := math.Hypot(float64(x-centerX), float64(y-centerY)*2)

// if distance is less than radius and greater than innerRadius, draw explosion char
if distance <= radius && distance >= innerRadius {
// Make placement random and less likely as explosion progresses
if rand.Float64() > progress {
// Pick a random explosion char
char := explosionChars[rand.Intn(len(explosionChars))]
buf.WriteRune(char)
} else {
buf.WriteRune(' ')
}
} else {
// If not explosion, then it's empty space
buf.WriteRune(' ')
}
}
// End of line
if y < height-1 {
buf.WriteRune('\n')
}
}

return buf.String()
}
2 changes: 1 addition & 1 deletion pkg/gui/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ func initialWindowViewNameMap(contextTree *context.ContextTree) *utils.ThreadSaf

func initialScreenMode(startArgs appTypes.StartArgs, config config.AppConfigurer) types.WindowMaximisation {
if startArgs.FilterPath != "" || startArgs.GitArg != appTypes.GitArgNone {
return types.SCREEN_HALF
return types.SCREEN_FULL
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a separate thing: I think if you do lazygit log you should just see the fullscreened commits view rather than half-sized

} else {
defaultWindowSize := config.GetUserConfig().Gui.WindowSize

Expand Down
1 change: 0 additions & 1 deletion pkg/integration/components/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ func (self *IntegrationTest) Run(gui integrationTypes.GuiDriver) {
// Setting caption to clear the options menu from whatever it starts with
testDriver.SetCaption("")
testDriver.SetCaptionPrefix("")
testDriver.Wait(1000)
}

self.run(testDriver, keys)
Expand Down
2 changes: 1 addition & 1 deletion pkg/integration/tests/demo/bisect.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var Bisect = NewIntegrationTest(NewIntegrationTestArgs{
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.SetCaptionPrefix("Git bisect")
t.Wait(1000)

markCommitAsBad := func() {
t.Views().Commits().
Expand All @@ -45,7 +46,6 @@ var Bisect = NewIntegrationTest(NewIntegrationTestArgs{

t.Views().Commits().
IsFocused().
Press(keys.Universal.NextScreenMode).
Tap(func() {
markCommitAsBad()

Expand Down
1 change: 1 addition & 0 deletions pkg/integration/tests/demo/cherry_pick.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.SetCaptionPrefix("Cherry pick commits from another branch")
t.Wait(1000)

t.Views().Branches().
Focus().
Expand Down
1 change: 1 addition & 0 deletions pkg/integration/tests/demo/commit_and_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var CommitAndPush = NewIntegrationTest(NewIntegrationTestArgs{
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.SetCaptionPrefix("Stage a file")
t.Wait(1000)

t.Views().Files().
IsFocused().
Expand Down
2 changes: 1 addition & 1 deletion pkg/integration/tests/demo/interactive_rebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ var InteractiveRebase = NewIntegrationTest(NewIntegrationTestArgs{
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.SetCaptionPrefix("Interactive rebase")
t.Wait(1000)

t.Views().Commits().
IsFocused().
Press(keys.Universal.NextScreenMode).
NavigateToLine(Contains("Add TypeScript types to User module")).
Press(keys.Universal.Edit).
SelectPreviousItem().
Expand Down
46 changes: 46 additions & 0 deletions pkg/integration/tests/demo/nuke_working_tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package demo

import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)

var NukeWorkingTree = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Nuke the working tree",
ExtraCmdArgs: []string{"status"},
Skip: false,
IsDemo: true,
SetupConfig: func(config *config.AppConfig) {
// No idea why I had to use version 2: it should be using my own computer's
// font and the one iterm uses is version 3.
config.UserConfig.Gui.NerdFontsVersion = "2"
config.UserConfig.Gui.AnimateExplosion = true
},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("blah")
shell.CreateFile("controllers/red_controller.rb", "")
shell.CreateFile("controllers/green_controller.rb", "")
shell.CreateFileAndAdd("controllers/blue_controller.rb", "")
shell.CreateFile("controllers/README.md", "")
shell.CreateFileAndAdd("views/helpers/list.rb", "")
shell.CreateFile("views/helpers/sort.rb", "")
shell.CreateFileAndAdd("views/users_view.rb", "")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.SetCaptionPrefix("Nuke the working tree")
t.Wait(1000)

t.Views().Files().
IsFocused().
Wait(1000).
Press(keys.Files.ViewResetOptions).
Tap(func() {
t.Wait(1000)

t.ExpectPopup().Menu().
Title(Equals("")).
Select(Contains("Nuke working tree")).
Confirm()
})
},
})
1 change: 1 addition & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ var tests = []*components.IntegrationTest{
demo.CherryPick,
demo.CommitAndPush,
demo.InteractiveRebase,
demo.NukeWorkingTree,
diff.Diff,
diff.DiffAndApplyPatch,
diff.DiffCommits,
Expand Down
1 change: 1 addition & 0 deletions test/default_test_config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ gui:
- reverse
# Not important in tests but it creates clutter in demos
showRandomTip: false
animateExplosion: false # takes too long
git:
# We don't want to run any periodic background git commands because it'll introduce race conditions and flakiness.
# If we need to refresh something from within the test (which should only really happen if we've invoked a
Expand Down