options.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package tea
  2. import (
  3. "context"
  4. "io"
  5. "sync/atomic"
  6. "github.com/muesli/termenv"
  7. )
  8. // ProgramOption is used to set options when initializing a Program. Program can
  9. // accept a variable number of options.
  10. //
  11. // Example usage:
  12. //
  13. // p := NewProgram(model, WithInput(someInput), WithOutput(someOutput))
  14. type ProgramOption func(*Program)
  15. // WithContext lets you specify a context in which to run the Program. This is
  16. // useful if you want to cancel the execution from outside. When a Program gets
  17. // cancelled it will exit with an error ErrProgramKilled.
  18. func WithContext(ctx context.Context) ProgramOption {
  19. return func(p *Program) {
  20. p.ctx = ctx
  21. }
  22. }
  23. // WithOutput sets the output which, by default, is stdout. In most cases you
  24. // won't need to use this.
  25. func WithOutput(output io.Writer) ProgramOption {
  26. return func(p *Program) {
  27. if o, ok := output.(*termenv.Output); ok {
  28. p.output = o
  29. } else {
  30. p.output = termenv.NewOutput(output, termenv.WithColorCache(true))
  31. }
  32. }
  33. }
  34. // WithInput sets the input which, by default, is stdin. In most cases you
  35. // won't need to use this. To disable input entirely pass nil.
  36. //
  37. // p := NewProgram(model, WithInput(nil))
  38. func WithInput(input io.Reader) ProgramOption {
  39. return func(p *Program) {
  40. p.input = input
  41. p.inputType = customInput
  42. }
  43. }
  44. // WithInputTTY opens a new TTY for input (or console input device on Windows).
  45. func WithInputTTY() ProgramOption {
  46. return func(p *Program) {
  47. p.inputType = ttyInput
  48. }
  49. }
  50. // WithoutSignalHandler disables the signal handler that Bubble Tea sets up for
  51. // Programs. This is useful if you want to handle signals yourself.
  52. func WithoutSignalHandler() ProgramOption {
  53. return func(p *Program) {
  54. p.startupOptions |= withoutSignalHandler
  55. }
  56. }
  57. // WithoutCatchPanics disables the panic catching that Bubble Tea does by
  58. // default. If panic catching is disabled the terminal will be in a fairly
  59. // unusable state after a panic because Bubble Tea will not perform its usual
  60. // cleanup on exit.
  61. func WithoutCatchPanics() ProgramOption {
  62. return func(p *Program) {
  63. p.startupOptions |= withoutCatchPanics
  64. }
  65. }
  66. // WithoutSignals will ignore OS signals.
  67. // This is mainly useful for testing.
  68. func WithoutSignals() ProgramOption {
  69. return func(p *Program) {
  70. atomic.StoreUint32(&p.ignoreSignals, 1)
  71. }
  72. }
  73. // WithAltScreen starts the program with the alternate screen buffer enabled
  74. // (i.e. the program starts in full window mode). Note that the altscreen will
  75. // be automatically exited when the program quits.
  76. //
  77. // Example:
  78. //
  79. // p := tea.NewProgram(Model{}, tea.WithAltScreen())
  80. // if _, err := p.Run(); err != nil {
  81. // fmt.Println("Error running program:", err)
  82. // os.Exit(1)
  83. // }
  84. //
  85. // To enter the altscreen once the program has already started running use the
  86. // EnterAltScreen command.
  87. func WithAltScreen() ProgramOption {
  88. return func(p *Program) {
  89. p.startupOptions |= withAltScreen
  90. }
  91. }
  92. // WithMouseCellMotion starts the program with the mouse enabled in "cell
  93. // motion" mode.
  94. //
  95. // Cell motion mode enables mouse click, release, and wheel events. Mouse
  96. // movement events are also captured if a mouse button is pressed (i.e., drag
  97. // events). Cell motion mode is better supported than all motion mode.
  98. //
  99. // This will try to enable the mouse in extended mode (SGR), if that is not
  100. // supported by the terminal it will fall back to normal mode (X10).
  101. //
  102. // To enable mouse cell motion once the program has already started running use
  103. // the EnableMouseCellMotion command. To disable the mouse when the program is
  104. // running use the DisableMouse command.
  105. //
  106. // The mouse will be automatically disabled when the program exits.
  107. func WithMouseCellMotion() ProgramOption {
  108. return func(p *Program) {
  109. p.startupOptions |= withMouseCellMotion // set
  110. p.startupOptions &^= withMouseAllMotion // clear
  111. }
  112. }
  113. // WithMouseAllMotion starts the program with the mouse enabled in "all motion"
  114. // mode.
  115. //
  116. // EnableMouseAllMotion is a special command that enables mouse click, release,
  117. // wheel, and motion events, which are delivered regardless of whether a mouse
  118. // button is pressed, effectively enabling support for hover interactions.
  119. //
  120. // This will try to enable the mouse in extended mode (SGR), if that is not
  121. // supported by the terminal it will fall back to normal mode (X10).
  122. //
  123. // Many modern terminals support this, but not all. If in doubt, use
  124. // EnableMouseCellMotion instead.
  125. //
  126. // To enable the mouse once the program has already started running use the
  127. // EnableMouseAllMotion command. To disable the mouse when the program is
  128. // running use the DisableMouse command.
  129. //
  130. // The mouse will be automatically disabled when the program exits.
  131. func WithMouseAllMotion() ProgramOption {
  132. return func(p *Program) {
  133. p.startupOptions |= withMouseAllMotion // set
  134. p.startupOptions &^= withMouseCellMotion // clear
  135. }
  136. }
  137. // WithoutRenderer disables the renderer. When this is set output and log
  138. // statements will be plainly sent to stdout (or another output if one is set)
  139. // without any rendering and redrawing logic. In other words, printing and
  140. // logging will behave the same way it would in a non-TUI commandline tool.
  141. // This can be useful if you want to use the Bubble Tea framework for a non-TUI
  142. // application, or to provide an additional non-TUI mode to your Bubble Tea
  143. // programs. For example, your program could behave like a daemon if output is
  144. // not a TTY.
  145. func WithoutRenderer() ProgramOption {
  146. return func(p *Program) {
  147. p.renderer = &nilRenderer{}
  148. }
  149. }
  150. // WithANSICompressor removes redundant ANSI sequences to produce potentially
  151. // smaller output, at the cost of some processing overhead.
  152. //
  153. // This feature is provisional, and may be changed or removed in a future version
  154. // of this package.
  155. func WithANSICompressor() ProgramOption {
  156. return func(p *Program) {
  157. p.startupOptions |= withANSICompressor
  158. }
  159. }
  160. // WithFilter supplies an event filter that will be invoked before Bubble Tea
  161. // processes a tea.Msg. The event filter can return any tea.Msg which will then
  162. // get handled by Bubble Tea instead of the original event. If the event filter
  163. // returns nil, the event will be ignored and Bubble Tea will not process it.
  164. //
  165. // As an example, this could be used to prevent a program from shutting down if
  166. // there are unsaved changes.
  167. //
  168. // Example:
  169. //
  170. // func filter(m tea.Model, msg tea.Msg) tea.Msg {
  171. // if _, ok := msg.(tea.QuitMsg); !ok {
  172. // return msg
  173. // }
  174. //
  175. // model := m.(myModel)
  176. // if model.hasChanges {
  177. // return nil
  178. // }
  179. //
  180. // return msg
  181. // }
  182. //
  183. // p := tea.NewProgram(Model{}, tea.WithFilter(filter));
  184. //
  185. // if _,err := p.Run(); err != nil {
  186. // fmt.Println("Error running program:", err)
  187. // os.Exit(1)
  188. // }
  189. func WithFilter(filter func(Model, Msg) Msg) ProgramOption {
  190. return func(p *Program) {
  191. p.filter = filter
  192. }
  193. }
  194. // WithFPS sets a custom maximum FPS at which the renderer should run. If
  195. // less than 1, the default value of 60 will be used. If over 120, the FPS
  196. // will be capped at 120.
  197. func WithFPS(fps int) ProgramOption {
  198. return func(p *Program) {
  199. p.fps = fps
  200. }
  201. }