options.go 6.9 KB

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