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

feat: stopwatch #68

Merged
merged 3 commits into from Sep 9, 2021
Merged

feat: stopwatch #68

merged 3 commits into from Sep 9, 2021

Conversation

caarlos0
Copy link
Member

@caarlos0 caarlos0 commented Sep 4, 2021

follow up of #67, a simple stopwatch component

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
stopwatch/stopwatch.go Outdated Show resolved Hide resolved
@meowgorithm
Copy link
Member

meowgorithm commented Sep 5, 2021

So in cases like this, StopMsg won't stop the timer because stopwatch will currently still respond to TickMsgs coming through its update function:

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/charmbracelet/bubbles/stopwatch"
	tea "github.com/charmbracelet/bubbletea"
)

type model struct {
	stopwatch stopwatch.Model
}

func (m model) Init() tea.Cmd {
	return m.stopwatch.Init()
}

func (m model) View() string {
	return "Elapsed: " + m.stopwatch.View()
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "ctrl+c", "q":
			return m, tea.Quit
		case "s":
			return m, func() tea.Msg {
				return stopwatch.StopMsg{}
			}
		}
	}
	var cmd tea.Cmd
	m.stopwatch, cmd = m.stopwatch.Update(msg)
	return m, cmd
}

func main() {
	m := model{
		stopwatch: stopwatch.NewWithInternval(time.Millisecond),
	}

	if err := tea.NewProgram(m).Start(); err != nil {
		fmt.Println("Oh no, it didn't work:", err)
		os.Exit(1)
	}
}

Though technically we can stop the timer without StopMsg:

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/charmbracelet/bubbles/stopwatch"
	tea "github.com/charmbracelet/bubbletea"
)

type model struct {
	stopwatch stopwatch.Model
	runTimer  bool
}

func (m model) Init() tea.Cmd {
	return m.stopwatch.Init()
}

func (m model) View() string {
	return "Elapsed: " + m.stopwatch.View()
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "ctrl+c", "q":
			return m, tea.Quit
		case "s":
			// Toggle start/stop
			m.runTimer = !m.runTimer
			if m.runTimer {
				return m, m.stopwatch.Init()
			}
		}
	}

	var cmd tea.Cmd
	if m.runTimer {
		m.stopwatch, cmd = m.stopwatch.Update(msg)
	}
	return m, cmd
}

func main() {
	m := model{
		stopwatch: stopwatch.NewWithInterval(time.Millisecond),
		runTimer:  true,
	}

	if err := tea.NewProgram(m).Start(); err != nil {
		fmt.Println("Oh no, it didn't work:", err)
		os.Exit(1)
	}
}

…so I wonder if we need it at all? If we do, maybe Model.StopMsg() is a better choice since we don't need to send a command through the runtime?

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
@caarlos0
Copy link
Member Author

caarlos0 commented Sep 7, 2021

Make it a bit more feature-full:

  • start
  • stop
  • reset
  • get elapsed time
  • get if its running or not

usage now looks like this:

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/charmbracelet/bubbles/stopwatch"
	tea "github.com/charmbracelet/bubbletea"
)

type model struct {
	stopwatch stopwatch.Model
}

func (m model) Init() tea.Cmd {
	return m.stopwatch.Init()
}

func (m model) View() string {
	return "Elapsed: " + m.stopwatch.View()
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.String() {
		case "ctrl+c", "q":
			return m, tea.Quit
		case "r":
			return m, m.stopwatch.Reset()
		case "s":
			if m.stopwatch.Running() {
				return m, m.stopwatch.Stop()
			}
			return m, m.stopwatch.Start()
		}
	}
	var cmd tea.Cmd
	m.stopwatch, cmd = m.stopwatch.Update(msg)
	return m, cmd
}

func main() {
	m := model{
		stopwatch: stopwatch.NewWithInterval(time.Second),
	}

	if err := tea.NewProgram(m).Start(); err != nil {
		fmt.Println("Oh no, it didn't work:", err)
		os.Exit(1)
	}
}

thoughts?

I wonder if we should also have a Toggle method to automatically handle the start/stop thing.

@meowgorithm
Copy link
Member

meowgorithm commented Sep 7, 2021

Looks great! Yeah, we might as well add a Toggle method, then I think this is good to go.

Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
@meowgorithm meowgorithm added the enhancement New feature or request label Sep 9, 2021
@meowgorithm meowgorithm merged commit a7ea1bd into charmbracelet:master Sep 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants