19
19
import webbrowser
20
20
from os .path import isfile
21
21
from shutil import which
22
- from typing import Sequence , Union , Optional
22
+ from typing import Callable , Sequence , Union , Optional
23
23
24
24
import Utils
25
25
import settings
@@ -160,6 +160,9 @@ def launch(exe, in_terminal=False):
160
160
subprocess .Popen (exe )
161
161
162
162
163
+ refresh_components : Optional [Callable [[], None ]] = None
164
+
165
+
163
166
def run_gui ():
164
167
from kvui import App , ContainerLayout , GridLayout , Button , Label , ScrollBox , Widget
165
168
from kivy .core .window import Window
@@ -170,30 +173,16 @@ class Launcher(App):
170
173
base_title : str = "Archipelago Launcher"
171
174
container : ContainerLayout
172
175
grid : GridLayout
173
-
174
- _tools = {c .display_name : c for c in components if c .type == Type .TOOL }
175
- _clients = {c .display_name : c for c in components if c .type == Type .CLIENT }
176
- _adjusters = {c .display_name : c for c in components if c .type == Type .ADJUSTER }
177
- _miscs = {c .display_name : c for c in components if c .type == Type .MISC }
176
+ _tool_layout : Optional [ScrollBox ] = None
177
+ _client_layout : Optional [ScrollBox ] = None
178
178
179
179
def __init__ (self , ctx = None ):
180
180
self .title = self .base_title
181
181
self .ctx = ctx
182
182
self .icon = r"data/icon.png"
183
183
super ().__init__ ()
184
184
185
- def build (self ):
186
- self .container = ContainerLayout ()
187
- self .grid = GridLayout (cols = 2 )
188
- self .container .add_widget (self .grid )
189
- self .grid .add_widget (Label (text = "General" , size_hint_y = None , height = 40 ))
190
- self .grid .add_widget (Label (text = "Clients" , size_hint_y = None , height = 40 ))
191
- tool_layout = ScrollBox ()
192
- tool_layout .layout .orientation = "vertical"
193
- self .grid .add_widget (tool_layout )
194
- client_layout = ScrollBox ()
195
- client_layout .layout .orientation = "vertical"
196
- self .grid .add_widget (client_layout )
185
+ def _refresh_components (self ) -> None :
197
186
198
187
def build_button (component : Component ) -> Widget :
199
188
"""
@@ -218,14 +207,47 @@ def build_button(component: Component) -> Widget:
218
207
return box_layout
219
208
return button
220
209
210
+ # clear before repopulating
211
+ assert self ._tool_layout and self ._client_layout , "must call `build` first"
212
+ tool_children = reversed (self ._tool_layout .layout .children )
213
+ for child in tool_children :
214
+ self ._tool_layout .layout .remove_widget (child )
215
+ client_children = reversed (self ._client_layout .layout .children )
216
+ for child in client_children :
217
+ self ._client_layout .layout .remove_widget (child )
218
+
219
+ _tools = {c .display_name : c for c in components if c .type == Type .TOOL }
220
+ _clients = {c .display_name : c for c in components if c .type == Type .CLIENT }
221
+ _adjusters = {c .display_name : c for c in components if c .type == Type .ADJUSTER }
222
+ _miscs = {c .display_name : c for c in components if c .type == Type .MISC }
223
+
221
224
for (tool , client ) in itertools .zip_longest (itertools .chain (
222
- self ._tools .items (), self ._miscs .items (), self ._adjusters .items ()), self ._clients .items ()):
225
+ _tools .items (), _miscs .items (), _adjusters .items ()
226
+ ), _clients .items ()):
223
227
# column 1
224
228
if tool :
225
- tool_layout .layout .add_widget (build_button (tool [1 ]))
229
+ self . _tool_layout .layout .add_widget (build_button (tool [1 ]))
226
230
# column 2
227
231
if client :
228
- client_layout .layout .add_widget (build_button (client [1 ]))
232
+ self ._client_layout .layout .add_widget (build_button (client [1 ]))
233
+
234
+ def build (self ):
235
+ self .container = ContainerLayout ()
236
+ self .grid = GridLayout (cols = 2 )
237
+ self .container .add_widget (self .grid )
238
+ self .grid .add_widget (Label (text = "General" , size_hint_y = None , height = 40 ))
239
+ self .grid .add_widget (Label (text = "Clients" , size_hint_y = None , height = 40 ))
240
+ self ._tool_layout = ScrollBox ()
241
+ self ._tool_layout .layout .orientation = "vertical"
242
+ self .grid .add_widget (self ._tool_layout )
243
+ self ._client_layout = ScrollBox ()
244
+ self ._client_layout .layout .orientation = "vertical"
245
+ self .grid .add_widget (self ._client_layout )
246
+
247
+ self ._refresh_components ()
248
+
249
+ global refresh_components
250
+ refresh_components = self ._refresh_components
229
251
230
252
Window .bind (on_drop_file = self ._on_drop_file )
231
253
@@ -254,10 +276,17 @@ def _stop(self, *largs):
254
276
255
277
Launcher ().run ()
256
278
279
+ # avoiding Launcher reference leak
280
+ # and don't try to do something with widgets after window closed
281
+ global refresh_components
282
+ refresh_components = None
283
+
257
284
258
285
def run_component (component : Component , * args ):
259
286
if component .func :
260
287
component .func (* args )
288
+ if refresh_components :
289
+ refresh_components ()
261
290
elif component .script_name :
262
291
subprocess .run ([* get_exe (component .script_name ), * args ])
263
292
else :
0 commit comments