1+ from collections import OrderedDict
12from dataclasses import dataclass
23import os
4+ import webbrowser
35from pathlib import Path
4- from typing import Any , Callable , Optional
6+ from typing import Any , Callable , Literal , Optional , TypeAlias , Union
57
68from roa import RoaCategoriesFile , RoaCategory , RoaEntry , RoaOrderFile
79
1214
1315ROA_DIR = Path (f"{ os .environ ['LOCALAPPDATA' ]} /RivalsofAether/workshop" )
1416
17+ Direction : TypeAlias = Union [Literal [1 ], Literal [- 1 ]]
18+
1519@dataclass
1620class ListboxItem ():
1721 label : str
@@ -42,23 +46,45 @@ def __init__(
4246
4347 self .items : list [ListboxItem ] = []
4448
49+ myscroll = tk .Scrollbar (self )
50+ myscroll .pack (side = tk .RIGHT , fill = tk .Y )
51+
4552 self .listbox = tk .Listbox (
4653 self ,
4754 relief = tk .GROOVE ,
4855 selectmode = selectmode ,
49- exportselection = False
56+ exportselection = False ,
57+ yscrollcommand = myscroll .set
5058 )
5159
52- self .listbox .grid (sticky = tk .NSEW )
60+ myscroll .config (command = self .listbox .yview )
61+
62+ self .listbox .pack (side = tk .TOP , fill = tk .Y , expand = 1 )
5363
5464 def set_items (self , items : list [ListboxItem ]):
5565 self .items = items
56- print (items )
5766 self .listbox .configure (state = tk .NORMAL )
5867 self .listbox .delete (0 , self .listbox .size ())
5968 for i in self .items :
6069 self .listbox .insert (tk .END , i .label )
6170
71+ def move_selected_items (self , sel_indexes , direction : Direction ) -> list [ListboxItem ]:
72+ selected_items = [self .items [si ] for si in sel_indexes ]
73+ if direction == 1 :
74+ selected_items = reversed (selected_items )
75+ for item in selected_items :
76+ i = self .items .index (item )
77+ if (i + direction ) < 0 or (i + direction ) >= len (self .items ):
78+ continue
79+
80+ self .items [i ], self .items [i + direction ] = self .items [i + direction ], self .items [i ]
81+
82+ self .listbox .delete (i )
83+ self .listbox .insert (i + direction , item .label )
84+
85+ self .listbox .selection_set (i + direction )
86+
87+ return self .items
6288
6389
6490class CharacterManager (tk .Tk ):
@@ -92,11 +118,10 @@ def widget_buttons_middle(self) -> tk.Frame:
92118 frame = tk .Frame (self )
93119 y = Counter ()
94120
95- btn_export = ttk .Button (frame , text = "Export to ROA" ,
96- command = self .save_state_to_roas )
121+ btn_export = ttk .Button (
122+ frame , text = "Export to ROA" ,
123+ command = self .save_state_to_roas )
97124 btn_export .grid (row = y .inc (), sticky = tk .EW )
98- btn_two = ttk .Button (frame , text = "2" )
99- btn_two .grid (row = y .inc (), sticky = tk .EW )
100125
101126 return frame
102127
@@ -107,39 +132,63 @@ def widget_buttons_cats(self) -> tk.Frame:
107132 # btn_open = ttk.Button(frame, text="Open", command=self.open_selected_category)
108133 # btn_open.grid(row=y.inc(), sticky=tk.EW)
109134
110- btn_2 = ttk .Button (frame , text = "2" )
111- btn_2 .grid (row = y .inc (), sticky = tk .EW )
135+ frame_updown = tk .Frame (frame )
136+
137+ btn_move_up = ttk .Button (frame_updown , text = "^" , command = self .fac_move_selected_cat (- 1 ))
138+ btn_move_up .grid (row = 0 , column = 0 )
139+
140+ btn_move_down = ttk .Button (frame_updown , text = "v" , command = self .fac_move_selected_cat (1 ))
141+ btn_move_down .grid (row = 0 , column = 1 )
112142
113- btn_move_up = ttk .Button (frame , text = "^" )
114- btn_move_up .grid (row = y .inc (), sticky = tk .EW )
143+ frame_updown .grid (row = y .inc (), sticky = tk .EW )
115144
116- btn_move_up = ttk .Button (frame , text = "v" )
117- btn_move_up .grid (row = y .inc (), sticky = tk .EW )
145+ btn_add = ttk .Button (frame , text = "TODO Add" )
146+ btn_add .grid (row = y .inc (), sticky = tk .EW )
147+
148+ btn_del = ttk .Button (frame , text = "TODO Delete" )
149+ btn_del .grid (row = y .inc (), sticky = tk .EW )
150+
151+ btn_rename = ttk .Button (frame , text = "TODO Rename" )
152+ btn_rename .grid (row = y .inc (), sticky = tk .EW )
118153
119154 return frame
120155
121156 def widget_buttons_chars (self ) -> tk .Frame :
122157 frame = tk .Frame (self )
123158 y = Counter ()
124159
160+ frame_updown = tk .Frame (frame )
125161 btn_move_up = ttk .Button (
126- frame , text = "^" ,
162+ frame_updown , text = "^" ,
127163 command = self .fac_move_selected_chars (- 1 )
128164 )
129- btn_move_up .grid (row = y . inc (), sticky = tk . EW )
165+ btn_move_up .grid (row = 0 , column = 0 )
130166
131167 btn_move_down = ttk .Button (
132- frame , text = "v" ,
168+ frame_updown , text = "v" ,
133169 command = self .fac_move_selected_chars (1 )
134170 )
135- btn_move_down .grid (row = y .inc (), sticky = tk .EW )
171+ btn_move_down .grid (row = 0 , column = 1 )
172+
173+ frame_updown .grid (row = y .inc (), sticky = tk .EW )
136174
137175 btn_sort_alpha = ttk .Button (
138176 frame , text = "Sort: A-Z" ,
139177 command = self .fac_sort_chars_by (sort_name )
140178 )
141179 btn_sort_alpha .grid (row = y .inc (), sticky = tk .EW )
142180
181+ btn_char_info = ttk .Button (
182+ frame , text = "Open in Steam" ,
183+ command = self .open_info
184+ )
185+ btn_char_info .grid (row = y .inc (), sticky = tk .EW )
186+
187+ btn_char_movecat = ttk .Button (
188+ frame , text = "TODO Move to..." ,
189+ # command=
190+ )
191+ btn_char_movecat .grid (row = y .inc (), sticky = tk .EW )
143192 return frame
144193
145194 def initwindow (self ) -> None :
@@ -159,17 +208,16 @@ def initwindow(self) -> None:
159208
160209 self .list_cats .listbox .bind ('<<ListboxSelect>>' , self .open_selected_category )
161210
162-
163211 frame_buttons_mid : tk .Frame = self .widget_buttons_middle ()
164212 frame_buttons_mid .grid (row = 0 , column = 1 )
165213
166214 self .list_chars : ItemListFrame = ItemListFrame (self , selectmode = tk .MULTIPLE )
167- self .list_chars .grid (row = 0 , column = 2 )
215+ self .list_chars .grid (row = 0 , column = 2 , sticky = tk . NS )
168216 frame_buttons_chars : tk .Frame = self .widget_buttons_chars ()
169217 frame_buttons_chars .grid (row = 1 , column = 2 )
170218
171219 lab_context_label = ttk .Label (self , textvariable = self .text_status , relief = tk .GROOVE )
172- lab_context_label .grid (row = 2 , column = 0 , columnspan = 3 , sticky = "EW" )
220+ lab_context_label .grid (row = 2 , column = 0 , columnspan = 3 , sticky = tk . SW )
173221
174222 # State management
175223 def gen_listitems_categories (self ) -> list [ListboxItem ]:
@@ -194,14 +242,13 @@ def save_state_to_roas(self, event=None) -> None:
194242 if len (char_list ) < 1 :
195243 continue
196244 new_cat = RoaCategory (len (characters ), label .encode ('utf-8' ))
197- print (new_cat )
198245 categories_roa .categories .append (new_cat )
199246 for char in char_list :
200247 try :
201248 characters .append (char )
202249 except KeyError :
203250 if label == '_removed' : continue
204- print ( "Couldn't find" , char )
251+ self . log ( f "Couldn't find { char } " )
205252 raise
206253
207254 order_roa .groups ['characters' ] = characters
@@ -246,26 +293,37 @@ def do_sort(event=None):
246293
247294 return do_sort
248295
249- def fac_move_selected_chars (self , direction : int ) -> Callable [..., None ]:
296+ def fac_move_selected_chars (self , direction : Direction ) -> Callable [..., None ]:
250297 def do_move (event = None ):
251- c = self .get_selected_category ()
252- print (self .nested_state [c ])
253-
254- # Reorder nested state
298+ category = self .get_selected_category ()
255299 sel_indexes = self .list_chars .listbox .curselection ()
256- selected_items = [self .nested_state [c ][si ] for si in sel_indexes ]
257- for item in selected_items :
258- i = self .nested_state [c ].index (item )
259- self .nested_state [c ][i ], self .nested_state [c ][i + direction ] = self .nested_state [c ][i + direction ], self .nested_state [c ][i ]
260- self .log (f"Moved { item !r} from { i } to { i + direction } " )
300+ reordered_items : list [ListboxItem ] = self .list_chars .move_selected_items (sel_indexes , direction )
301+ self .nested_state [category ] = [i .value for i in reordered_items ]
302+ assert self .list_chars .items == self .gen_listboxitems_chars (category )
303+ return do_move
304+
305+ def fac_move_selected_cat (self , direction : Direction ) -> Callable [..., None ]:
306+ def do_move (event = None ):
307+ (sel_index ,) = self .list_cats .listbox .curselection ()
308+ if (sel_index + direction ) < 0 or (sel_index + direction ) >= len (self .nested_state .keys ()):
309+ return
310+ reordered_items : list [ListboxItem ] = self .list_cats .move_selected_items ([sel_index ], direction )
261311
262- # TODO: Edit list inplace
263- self .open_category (c )
264- print (self .nested_state [c ])
265- assert self .list_chars .items == self .gen_listboxitems_chars (c )
312+ tups = list (self .nested_state .items ())
313+ tups [sel_index ], tups [sel_index + direction ] = tups [sel_index + direction ], tups [sel_index ]
314+ self .nested_state = OrderedDict (tups )
266315
316+ self .log (f"New key order: { self .nested_state .keys ()} " )
317+ assert reordered_items == self .gen_listitems_categories ()
267318 return do_move
268319
320+ def open_info (self , event = None ):
321+ sel_index , * _ = self .list_chars .listbox .curselection ()
322+ char : RoaEntry = self .list_chars .items [sel_index ].value
323+ url = f"steam://openurl/https://steamcommunity.com/sharedfiles/filedetails/?id={ char .id } "
324+ webbrowser .open (url , autoraise = True )
325+
326+
269327if __name__ == '__main__' :
270328 order_roa = RoaOrderFile (ROA_DIR / 'order.roa' )
271329 categories_roa = RoaCategoriesFile (ROA_DIR / 'categories.roa' )
0 commit comments