dialogs.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """
  2. Collection of reusable components for building full screen applications.
  3. """
  4. from __future__ import annotations
  5. from typing import Sequence
  6. from prompt_toolkit.filters import has_completions, has_focus
  7. from prompt_toolkit.formatted_text import AnyFormattedText
  8. from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
  9. from prompt_toolkit.key_binding.key_bindings import KeyBindings
  10. from prompt_toolkit.layout.containers import (
  11. AnyContainer,
  12. DynamicContainer,
  13. HSplit,
  14. VSplit,
  15. )
  16. from prompt_toolkit.layout.dimension import AnyDimension
  17. from prompt_toolkit.layout.dimension import Dimension as D
  18. from .base import Box, Button, Frame, Shadow
  19. __all__ = [
  20. "Dialog",
  21. ]
  22. class Dialog:
  23. """
  24. Simple dialog window. This is the base for input dialogs, message dialogs
  25. and confirmation dialogs.
  26. Changing the title and body of the dialog is possible at runtime by
  27. assigning to the `body` and `title` attributes of this class.
  28. :param body: Child container object.
  29. :param title: Text to be displayed in the heading of the dialog.
  30. :param buttons: A list of `Button` widgets, displayed at the bottom.
  31. """
  32. def __init__(
  33. self,
  34. body: AnyContainer,
  35. title: AnyFormattedText = "",
  36. buttons: Sequence[Button] | None = None,
  37. modal: bool = True,
  38. width: AnyDimension = None,
  39. with_background: bool = False,
  40. ) -> None:
  41. self.body = body
  42. self.title = title
  43. buttons = buttons or []
  44. # When a button is selected, handle left/right key bindings.
  45. buttons_kb = KeyBindings()
  46. if len(buttons) > 1:
  47. first_selected = has_focus(buttons[0])
  48. last_selected = has_focus(buttons[-1])
  49. buttons_kb.add("left", filter=~first_selected)(focus_previous)
  50. buttons_kb.add("right", filter=~last_selected)(focus_next)
  51. frame_body: AnyContainer
  52. if buttons:
  53. frame_body = HSplit(
  54. [
  55. # Add optional padding around the body.
  56. Box(
  57. body=DynamicContainer(lambda: self.body),
  58. padding=D(preferred=1, max=1),
  59. padding_bottom=0,
  60. ),
  61. # The buttons.
  62. Box(
  63. body=VSplit(buttons, padding=1, key_bindings=buttons_kb),
  64. height=D(min=1, max=3, preferred=3),
  65. ),
  66. ]
  67. )
  68. else:
  69. frame_body = body
  70. # Key bindings for whole dialog.
  71. kb = KeyBindings()
  72. kb.add("tab", filter=~has_completions)(focus_next)
  73. kb.add("s-tab", filter=~has_completions)(focus_previous)
  74. frame = Shadow(
  75. body=Frame(
  76. title=lambda: self.title,
  77. body=frame_body,
  78. style="class:dialog.body",
  79. width=(None if with_background is None else width),
  80. key_bindings=kb,
  81. modal=modal,
  82. )
  83. )
  84. self.container: Box | Shadow
  85. if with_background:
  86. self.container = Box(body=frame, style="class:dialog", width=width)
  87. else:
  88. self.container = frame
  89. def __pt_container__(self) -> AnyContainer:
  90. return self.container