ya2 · news · projects · code · about

housekeeping: ya2.utils.gui
[pmachines.git] / ya2 / utils / gui / base_page.py
CommitLineData
9199c6aa
FC
1from dataclasses import dataclass
2from panda3d.core import TextNode
3from direct.gui.OnscreenText import OnscreenText
4from direct.gui.DirectGui import DirectButton, DirectOptionMenu, DirectSlider, DirectCheckButton
5from direct.gui.DirectGuiGlobals import FLAT
6from ya2.utils.audio import AudioTools
7from ya2.utils.gfx import DirectGuiMixin, GfxTools
8from ya2.utils.gui.gui import GuiTools
9
10
11class DirectOptionMenuTestable(DirectOptionMenu):
12
13 def __init__(self, parent=None, **kw):
14 DirectOptionMenu.__init__(self, parent, **kw)
15 self.initialiseoptions(DirectOptionMenuTestable)
16
17 def showPopupMenu(self, event=None):
18 #super().showPopupMenu(event) # it does not work with mixins
19 DirectOptionMenu.showPopupMenu(self, event)
20 self._show_cb([self.component(c) for c in self.components()])
21
22
23@dataclass
9f095a28 24class BaseArgs:
9199c6aa
FC
25 id: ... = ''
26 text: ... = None
27 pos: ... = None
28 scale: ... = None
29 wordwrap: ... = None
30 texture: ... = None
31 text_scale: ... = None
32 font: ... = None
33 color: ... = None
34
35 def to_p3d_args(self):
36 d = {}
37 d['text'] = self.text
38 d['pos'] = self.pos or (0, 1, 0)
39 d['scale'] = self.scale or .12
40 d['font'] = self.font or GuiTools.load_font('assets/fonts/Hanken-Book.ttf')
41 d['fg'] = self.color or (.9, .9, .9, 1)
42 return d
43
44
45@dataclass
9f095a28 46class ButtonArgs(BaseArgs):
9199c6aa
FC
47 command: ... = None
48 command_args: ... = None
49 size: ... = None
50 text_color: ... = None
51 relief: ... = None
52 rollover_sound: ... = None
53 click_sound: ... = None
54
55 def to_p3d_args(self):
56 d = super().to_p3d_args()
57 d['frameSize'] = self.size or (-4.8, 4.8, -.6, 1.2)
58 d['command'] = self.command
59 d['extraArgs'] = self.command_args or []
60 d['text_fg'] = self.text_color or d.pop('fg')
61 if 'fg' in d: del d['fg']
62 d['text_font'] = self.font or d.pop('font')
63 d['frameColor'] = self.color or (.4, .4, .4, .14)
64 d['relief'] = self.relief or FLAT
65 d['rolloverSound'] = self.rollover_sound or AudioTools.load_sfx('assets/audio/sfx/rollover.ogg')
66 d['clickSound'] = self.click_sound or AudioTools.load_sfx('assets/audio/sfx/click.ogg')
67 d['pos'] = (self.pos[0], 1, self.pos[1]) if len(self.pos) == 2 else self.pos
68 d['text_wordwrap'] = self.wordwrap
69 d['frameTexture'] = self.texture
70 d['text_scale'] = self.text_scale or 1
71 return d
72
73 def __build_gui_args(self):
74 base = {
75 'scale': .12,
76 'text_font': GuiTools.load_font('assets/fonts/Hanken-Book.ttf'),
77 'text_fg': (.9, .9, .9, 1),
78 'relief': FLAT,
79 'frameColor': (.4, .4, .4, .14),
80 'rolloverSound': AudioTools.load_sfx('assets/audio/sfx/rollover.ogg'),
81 'clickSound': AudioTools.load_sfx('assets/audio/sfx/click.ogg')}
82 button = {'size': (-4.8, 4.8, -.6, 1.2)} | base
83 return button
84
85
86@dataclass
9f095a28 87class SliderArgs(ButtonArgs):
9199c6aa
FC
88 range: ... = None
89 scale: ... = None
90 value: ... = None
91
92 def to_p3d_args(self):
93 d = super().to_p3d_args()
94 d['range'] = self.range or (0, 1)
95 d['thumb_frameColor'] = (.4, .4, .4, .4)
96 d['thumb_scale'] = 1.6
97 d['thumb_relief'] = d['relief']
98 d['scale'] = self.scale or .4
99 d['value'] = self.value or 0
100 del d['rolloverSound']
101 del d['clickSound']
102 del d['frameSize']
103 return d
104
105
106@dataclass
9f095a28 107class OptionMenuArgs(ButtonArgs):
9199c6aa
FC
108 items: ... = None
109 initial_item: ... = None
110 item_color: ... = None
111 popup_color: ... = None
112 item_relief: ... = None
113 item_text_font: ... = None
114 item_text_fg: ... = None
115 highlight_color: ... = None
116 text_align: ... = None
117
118 def to_p3d_args(self):
119 d = super().to_p3d_args()
120 d['items'] = self.items
121 d['initialitem'] = self.initial_item
122 d['item_frameColor'] = d['frameColor']
123 d['popupMarker_frameColor'] = d['frameColor']
124 d['popupMarker_relief'] = d['relief']
125 d['item_relief'] = d['relief']
126 d['item_text_font'] = d['text_font']
127 d['item_text_fg'] = d['text_fg']
128 d['textMayChange'] = 1
129 h = d['frameColor']
130 h = (h[0] + .2, h[1] + .2, h[2] + .2, h[3] + .2)
131 d['highlightColor'] = self.highlight_color or h
132 d['text_align'] = TextNode.A_center
133 d['frameSize'] = (f:=d['frameSize'])[0], f[1] - .56, f[2], f[3]
134 return d
135
136
137@dataclass
9f095a28 138class CheckButtonArgs(ButtonArgs):
9199c6aa
FC
139 value: ... = None
140
141 def to_p3d_args(self):
142 d = super().to_p3d_args()
143 h = d['frameColor']
144 h = (h[0] + .2, h[1] + .2, h[2] + .2, h[3] + .2)
145 d['indicator_frameColor'] = h
146 d['indicator_relief'] = d['relief']
147 d['indicatorValue'] = self.value or 0
148 return d
149
150
151@dataclass
9f095a28 152class TextArgs(BaseArgs):
9199c6aa
FC
153 align: ... = None
154
155 def to_p3d_args(self):
156 d = super().to_p3d_args()
157 if self.align:
158 a = {'left': TextNode.A_left,
159 'right': TextNode.A_right,
160 'center': TextNode.A_center}[self.align]
161 d['align'] = a
162 return d
163
164
165class BasePage:
166
167 def __init__(self, page_info):
168 self.__widgets = []
169 self._page_info = page_info
170 for k in list(self._page_info.test_positions.keys()):
171 del self._page_info.test_positions[k]
172 self._build_widgets()
173
174 def _add_text(self, text_info):
175 t = OnscreenText(
176 **text_info.to_p3d_args())
177 self.__widgets += [t]
178 return t
179
180 def _add_button(self, button_args, vertically_centered=False):
181 b = DirectButton(**button_args.to_p3d_args())
182 b.__class__ = type('DirectButtonMixed', (DirectButton, DirectGuiMixin), {})
183 self._page_info.test_positions[button_args.id] = b.pos_pixel()
184 self.__widgets += [b]
185 if vertically_centered:
186 for j in range(4):
187 tnode = b.component('text%s' % j).textNode
188 height = - tnode.getLineHeight() / 2
189 height += (tnode.get_height() - tnode.get_line_height()) / 2
190 b.component('text%s' % j).set_pos(0, 0, height)
191 return b
192
193 def _add_check_button(self, button_args):
194 b = DirectCheckButton(**button_args.to_p3d_args())
195 b.__class__ = type('DirectCheckButtonMixed', (DirectCheckButton, DirectGuiMixin), {})
196 self.__widgets += [b]
197 self._page_info.test_positions[button_args.id] = b.pos_pixel()
198 return b
199
200 def _add_option_menu(self, option_args, cb):
201 o = DirectOptionMenuTestable(**option_args.to_p3d_args())
202 o.__class__ = type('DirectOptionMenuMixed', (DirectOptionMenu, DirectGuiMixin), {})
203 o._show_cb = cb
204 o.popupMenu['frameColor'] = option_args.to_p3d_args()['frameColor']
205 o.popupMenu['relief'] = option_args.to_p3d_args()['relief']
206 self.__widgets += [o]
207 self._page_info.test_positions[option_args.id] = o.pos_pixel()
208 return o
209
210 def _add_slider(self, slider_args):
211 s = DirectSlider(**slider_args.to_p3d_args())
212 s.__class__ = type('DirectSliderMixed', (DirectSlider, DirectGuiMixin), {})
213 self._page_info.test_positions['volume'] = s.pos_pixel()
214 np_left = GfxTools.build_empty_node('left_slider')
215 np_left.set_pos(s.get_net_transform().get_pos())
216 np_left.set_x(np_left.get_x() - s.get_scale()[0])
217 lpos = np_left.pos_as_widget() # try with pos2d_pixel and remove pos_as_widget if ok
218 self.__widgets += [s]
219 self._page_info.test_positions['volume_0'] = lpos
220 return s
221
222 def _set_page(self, page_name):
223 self._page_info.set_page(page_name)
224 self.destroy()
225
226 def on_back(self):
227 self._set_page('main')
228
229 def _rearrange_buttons_width(self):
230 max_width = 0
231 for w in self.__widgets:
232 t = w.component('text0')
233 ul = t.textNode.get_upper_left_3d()
234 lr = t.textNode.get_lower_right_3d()
235 max_width = max(lr[0] - ul[0], max_width)
236 for w in self.__widgets:
237 m_w = max_width / 2 + .8
238 w['frameSize'] = -m_w, m_w, w['frameSize'][2], w['frameSize'][3]
239
240 def destroy(self):
241 for w in self.__widgets: w.destroy()
242 self._page_info = None
243 self.__widgets = []