You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
11 KiB

3 years ago
  1. """
  2. Houses all the supporting rewx components for
  3. the main application window.
  4. """
  5. import wx # type: ignore
  6. from typing_extensions import TypedDict
  7. from gooey.gui.components.config import ConfigPage, TabbedConfigPage
  8. from gooey.gui.components.console import Console
  9. from gooey.gui.components.mouse import notifyMouseEvent
  10. from gooey.gui.components.sidebar import Sidebar
  11. from gooey.gui.components.tabbar import Tabbar
  12. from gooey.gui.lang.i18n import _
  13. from gooey.gui.pubsub import pub
  14. from gooey.gui.state import present_time
  15. from gooey.gui.three_to_four import Constants
  16. from gooey.python_bindings import constants
  17. from rewx import components as c # type: ignore
  18. from rewx import wsx, mount, update # type: ignore
  19. from rewx.core import Component, Ref # type: ignore
  20. from rewx.widgets import set_basic_props # type: ignore
  21. def attach_notifier(parent):
  22. """
  23. Recursively attaches the mouseEvent notifier
  24. to all elements in the tree
  25. """
  26. parent.Bind(wx.EVT_LEFT_DOWN, notifyMouseEvent)
  27. for child in parent.Children:
  28. attach_notifier(child)
  29. class HeaderProps(TypedDict):
  30. background_color: str
  31. title: str
  32. show_title: bool
  33. subtitle: str
  34. show_subtitle: bool
  35. class RHeader(Component):
  36. def __init__(self, props):
  37. super().__init__(props)
  38. self.parentRef = Ref()
  39. def component_did_mount(self):
  40. attach_notifier(self.parentRef.instance)
  41. def render(self):
  42. if 'running' not in self.props['image_uri']:
  43. imageProps = {
  44. 'uri': self.props['image_uri'],
  45. 'size': self.props['image_size'],
  46. 'flag': wx.RIGHT,
  47. 'border': 10}
  48. else:
  49. imageProps = {
  50. 'size': self.props['image_size'],
  51. 'flag': wx.RIGHT,
  52. 'border': 10}
  53. return wsx(
  54. [c.Block, {'orient': wx.HORIZONTAL,
  55. 'ref': self.parentRef,
  56. 'min_size': (120, self.props['height']),
  57. 'background_color': self.props['background_color']},
  58. [c.Block, {'orient': wx.VERTICAL,
  59. 'flag': wx.ALIGN_CENTER_VERTICAL | wx.ALL,
  60. 'proportion': 1,
  61. 'border': 10},
  62. [TitleText, {'label': self.props['title'],
  63. 'show': self.props['show_title'],
  64. 'wx_name': 'header_title'}],
  65. [c.StaticText, {'label': self.props['subtitle'],
  66. 'show': self.props['show_subtitle'],
  67. 'wx_name': 'header_subtitle'}]],
  68. [c.StaticBitmap, imageProps]]
  69. )
  70. class RFooter(Component):
  71. def __init__(self, props):
  72. super().__init__(props)
  73. self.ref = Ref()
  74. def component_did_mount(self):
  75. """
  76. We have to manually wire up LEFT_DOWN handlers
  77. for every component due to wx limitations.
  78. See: mouse.py docs for background.
  79. """
  80. block: wx.BoxSizer = self.ref.instance
  81. attach_notifier(block)
  82. def handle(self, btn):
  83. def inner(*args, **kwargs):
  84. pub.send_message(btn['id'])
  85. return inner
  86. def render(self):
  87. return wsx(
  88. [c.Block, {'orient': wx.VERTICAL,
  89. 'min_size': (30, 53),
  90. 'background_color': self.props['bg_color']},
  91. [c.Block, {'orient': wx.VERTICAL, 'proportion': 1}],
  92. [c.Block, {'orient': wx.HORIZONTAL,
  93. 'border': 20,
  94. 'flag': wx.EXPAND | wx.LEFT | wx.RIGHT,
  95. 'ref': self.ref},
  96. [c.Gauge, {'range': 100,
  97. 'proportion': 1,
  98. 'value': self.props['progress']['value'],
  99. 'show': self.props['progress']['show']}],
  100. [c.StaticText, {'label': present_time(self.props['timing']),
  101. 'flag': wx.LEFT,
  102. 'wx_name': 'timing',
  103. 'show': self.props['timing']['show'],
  104. 'border': 20}],
  105. [c.Block, {'orient': wx.HORIZONTAL, 'proportion': 1}],
  106. *[[c.Button, {**btn,
  107. 'label': _(btn['label_id']),
  108. 'min_size': (90, 23),
  109. 'flag': wx.LEFT,
  110. 'border': 10,
  111. 'on_click': self.handle(btn)
  112. }]
  113. for btn in self.props['buttons']]],
  114. [c.Block, {'orient': wx.VERTICAL, 'proportion': 1}]]
  115. )
  116. class RNavbar(Component):
  117. def __init__(self, props):
  118. super().__init__(props)
  119. # if self.buildSpec['navigation'] == constants.TABBED:
  120. # navigation = Tabbar(self, self.buildSpec, self.configs)
  121. # else:
  122. # navigation = Sidebar(self, self.buildSpec, self.configs)
  123. # if self.buildSpec['navigation'] == constants.HIDDEN:
  124. # navigation.Hide()
  125. def render(self):
  126. return wsx(
  127. )
  128. def VerticalSpacer(props):
  129. return wsx([c.Block, {'orient': wx.VERTICAL, 'min_size': (-1, props['height'])}])
  130. def SidebarControls(props):
  131. return wsx(
  132. [c.Block, {'orient': wx.VERTICAL,
  133. 'min_size': (180, 0),
  134. 'size': (180, 0),
  135. 'show': props.get('show', True),
  136. 'flag': wx.EXPAND,
  137. 'proportion': 0,
  138. 'background_color': props['bg_color']},
  139. [c.Block, {'orient': wx.VERTICAL,
  140. 'min_size': (180, 0),
  141. 'size': (180, 0),
  142. 'flag': wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM,
  143. 'border': 10,
  144. 'proportion': 1,
  145. 'background_color': props['bg_color']},
  146. [VerticalSpacer, {'height': 15}],
  147. [TitleText, {'label': props['label']}],
  148. [VerticalSpacer, {'height': 5}],
  149. [c.ListBox, {'choices': props['options'],
  150. 'value': props['activeSelection'],
  151. 'proportion': 1,
  152. 'on_change': props['on_change'],
  153. 'flag': wx.EXPAND}],
  154. [VerticalSpacer, {'height': 10}]]]
  155. )
  156. def ProgressSpinner(props):
  157. return wsx(
  158. [c.Block, {'flag': wx.EXPAND, 'show': props['show']},
  159. [c.Gauge, {'flag': wx.EXPAND,
  160. 'value': -1,
  161. 'size': (-1, 4)}],
  162. [c.StaticLine, {'style': wx.LI_HORIZONTAL,
  163. 'flag': wx.EXPAND}]]
  164. )
  165. def ErrorWarning(props):
  166. return wsx(
  167. [c.Block, {'orient': wx.HORIZONTAL,
  168. 'background_color': '#fdeded',
  169. 'style': wx.SIMPLE_BORDER,
  170. 'flag': wx.EXPAND | wx.ALL,
  171. 'proportion': 0,
  172. 'border': 5,
  173. 'min_size': (-1, 45),
  174. 'show': props.get('show', True)},
  175. [c.StaticBitmap, {'size': (24, 24),
  176. 'flag': wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
  177. 'border': 6,
  178. 'uri': props['uri']}],
  179. [c.StaticText, {'label': 'Whoops! You have some errors which must be corrected',
  180. 'flag': wx.ALIGN_CENTER_VERTICAL}]]
  181. )
  182. def RSidebar(props):
  183. return wsx(
  184. [c.Block, {'orient': wx.HORIZONTAL,
  185. 'show': props.get('show', True),
  186. 'flag': props['flag'],
  187. 'proportion': props['proportion'],
  188. 'ref': props['ref']},
  189. [SidebarControls, {**props, 'show': props['show_sidebar']}],
  190. [c.StaticLine, {'style': wx.LI_VERTICAL,
  191. 'flag': wx.EXPAND,
  192. 'min_size': (1, -1)}],
  193. *[[TabbedConfigPage if props['tabbed_groups'] else ConfigPage,
  194. {'flag': wx.EXPAND,
  195. 'proportion': 3,
  196. 'config': config,
  197. 'show': i == props['activeSelection']}]
  198. for i, config in enumerate(props['config'].values())]
  199. ]
  200. )
  201. def RTabbedLayout(props):
  202. return wsx(
  203. [c.Notebook, {'flag': wx.EXPAND | wx.ALL,
  204. 'show': props.get('show', True),
  205. 'proportion': 1,
  206. 'on_change': props['on_change'],
  207. 'ref': props['ref']},
  208. *[[c.NotebookItem,
  209. {'title': props['options'][i], 'selected': props['activeSelection'] == i},
  210. [TabbedConfigPage if props['tabbed_groups'] else ConfigPage,
  211. {'flag': wx.EXPAND,
  212. 'proportion': 3,
  213. 'config': config,
  214. 'show': i == props['activeSelection']}]]
  215. for i, config in enumerate(props['config'].values())]]
  216. )
  217. def layout_choose():
  218. def buildNavigation(self):
  219. """
  220. Chooses the appropriate layout navigation component based on user prefs
  221. """
  222. if self.buildSpec['navigation'] == constants.TABBED:
  223. navigation = Tabbar(self, self.buildSpec, self.configs)
  224. else:
  225. navigation = Sidebar(self, self.buildSpec, self.configs)
  226. if self.buildSpec['navigation'] == constants.HIDDEN:
  227. navigation.Hide()
  228. return navigation
  229. def buildConfigPanels(self, parent):
  230. page_class = TabbedConfigPage if self.buildSpec['tabbed_groups'] else ConfigPage
  231. return [page_class(parent, widgets, self.buildSpec)
  232. for widgets in self.buildSpec['widgets'].values()]
  233. class TitleText(Component):
  234. def __init__(self, props):
  235. super().__init__(props)
  236. self.ref = Ref()
  237. def component_did_mount(self):
  238. text: wx.StaticText = self.ref.instance
  239. font_size = text.GetFont().GetPointSize()
  240. text.SetFont(wx.Font(
  241. int(font_size * 1.2),
  242. wx.FONTFAMILY_DEFAULT,
  243. Constants.WX_FONTSTYLE_NORMAL,
  244. wx.FONTWEIGHT_BOLD,
  245. False
  246. ))
  247. def render(self):
  248. return wsx([c.StaticText, {**self.props, 'label': self.props['label'], 'ref': self.ref}])
  249. ##
  250. ## REWX definitions:
  251. ##
  252. @mount.register(ConfigPage) # type: ignore
  253. def config(element, parent):
  254. return update(element, ConfigPage(parent, element['props']['config'], {'contents': []}))
  255. @update.register(ConfigPage) # type: ignore
  256. def config(element, instance: ConfigPage):
  257. set_basic_props(instance, element['props'])
  258. return instance
  259. @mount.register(TabbedConfigPage) # type: ignore
  260. def tabbedconfig(element, parent):
  261. return update(element, TabbedConfigPage(parent, element['props']['config'], {'contents': []}))
  262. @update.register(TabbedConfigPage) # type: ignore
  263. def tabbedconfig(element, instance: TabbedConfigPage):
  264. set_basic_props(instance, element['props'])
  265. return instance
  266. @mount.register(Console) # type: ignore
  267. def console(element, parent):
  268. return update(element, Console(parent, element['props']))
  269. @update.register(Console) # type: ignore
  270. def console(element, instance: Console):
  271. set_basic_props(instance, element['props'])
  272. if 'show' in element['props']:
  273. instance.Show(element['props']['show'])
  274. return instance