-
Notifications
You must be signed in to change notification settings - Fork 6
/
CM_BrowserDialogs.cs
865 lines (784 loc) · 43 KB
/
CM_BrowserDialogs.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
using System;
using System.Collections.Generic;
using UnityEngine;
using ExtensionMethods;
using KatLib;
namespace CraftManager
{
//CMBrowserDialogs is inherited by the main CMBrowser class and contains all the popup dialogs which are used by the main interface.
public class CMBrowserDialogs : CMBrowserBase
{
//All these dialogs use the 'show_dialog' method which is lurking below them
//The show_dialog method takes care of all common aspects, leaving these dialog methods DRY and minimal
//The delegate passed to show_dialog is expected to return a string (resp), This is used to pass back
//error messages or a success status code ("200"). If "200" is returned the dialog will be closed,
//any other string will be shown as an error message to the user.
//Dialogs can also use a submit(<args>) method which draws a button that calls the given delegate, but also binds
//the delegate to be triggered by enter key press. the delagate must return a response string based on the success
//of the action which is then returned by submit.
protected void rename_craft_dialog(){
CraftData craft = CraftData.selected_craft;
if(craft != null){
craft.new_name = craft.name;
string resp = "";
show_dialog("Rename Craft", "rename: " + craft.name, d =>{
GUI.SetNextControlName("dialog_focus_field");
craft.new_name = GUILayout.TextField(craft.new_name, width(d.window_pos.width - 22));
section(() =>{
fspace();
button("Cancel", close_dialog);
resp = submit("Rename", craft.rename);
});
return resp;
});
}
}
protected void delete_craft_dialog(){
if(CraftData.active_craft.Count > 0){
string resp = "";
string message = "";
if(CraftData.active_craft.Count == 1){
message = "Delete " + CraftData.active_craft[0].name + "?";
} else{
message = "Delete " + CraftData.active_craft.Count + " selected craft?";
}
message += "\nAre you sure you want to do this?";
show_dialog("Delete Craft?", message, d =>{
section((w) =>{
fspace();
button("Cancel", close_dialog);
resp = submit("Delete", "button.delete", CraftData.delete_active_craft);
});
return resp;
});
}
}
protected void transfer_craft_dialog(){
if(CraftData.active_craft.Count > 0){
string resp = "";
bool switch_editor = false;
Dictionary<string, EditorFacility> lookup = new Dictionary<string, EditorFacility> { { "SPH", EditorFacility.SPH }, { "VAB", EditorFacility.VAB }, { "Subassembly", EditorFacility.None } };
int selected_transfer_option = -1;
if(CraftData.active_craft.Count == 1){
lookup.Remove(CraftData.active_craft[0].construction_type);
}
string[] transfer_options = new List<string>(lookup.Keys).ToArray();
string opt = null;
show_dialog("Transfer Craft", "Select where to Transfer " + (CraftData.active_craft.Count > 1 ? "these" : "this") + " craft to:", d =>{
section(() =>{
switch_editor = GUILayout.Toggle(switch_editor, "");
button("Switch editor after transfer", "bold", () =>{
switch_editor = !switch_editor;
});
});
section(() =>{
selected_transfer_option = GUILayout.SelectionGrid(selected_transfer_option, transfer_options, transfer_options.Length, "button.large");
});
if(selected_transfer_option != -1){
opt = transfer_options[selected_transfer_option];
selected_transfer_option = -1;
resp = CraftData.transfer_active_craft_to(lookup[opt]);
if(switch_editor){
if(CraftManager.version.Split('.')[1] == "0"){
EditorDriver.StartAndLoadVessel(CraftData.active_craft[0].path, lookup[opt]);
} else{
EditorLogic.fetch.SwitchEditor();
CraftData.select_craft(CraftData.active_craft[0]);
load_craft("load");
}
}
}
section(() =>{
fspace();
button("Cancel", close_dialog);
});
return resp;
});
}
}
protected void move_copy_craft_dialog(){
// CraftData craft = CraftData.selected_craft;
if(CraftData.active_craft.Count > 0){
string resp = "";
string selected_save = "";
DropdownMenuData move_copy_save_menu = new DropdownMenuData();
save_menu_data(move_copy_save_menu);
foreach(CraftData craft in CraftData.active_craft){
move_copy_save_menu.items.Remove(craft.save_dir);
}
move_copy_save_menu.items.Remove(all_saves_ref);
move_copy_save_menu.items.Remove("kerbalx_remote");
Rect d_offset = new Rect();
show_dialog("Move/Copy Craft", "Move or Copy " + (CraftData.active_craft.Count > 1 ? "these" : "this") + " craft to another save:", d =>{
section(500f, (inner_width) =>{
d_offset.x = -d.window_pos.x;
d_offset.y = -d.window_pos.y;
dropdown("Select Save", StyleSheet.assets["caret-down"], "copy_transfer_save_menu", move_copy_save_menu, d, d_offset, inner_width / 2, "button.large", "menu.background", "menu.item", (selected_save_name) =>{
resp = "";
selected_save = selected_save_name;
});
});
if(CraftData.active_craft.Count > 1){
label("if the save you're looking for is not listed, it is because one of the craft you've selected is already in that save", "small");
}
section(() =>{
label("Selected Save: ", "h2");
label(selected_save, "h2");
});
section(() =>{
button("Move", "button.large", () =>{
resp = CraftData.move_copy_active_craft_to(selected_save, true);
});
button("Copy", "button.large", () =>{
resp = CraftData.move_copy_active_craft_to(selected_save, false);
});
});
section(() =>{
fspace();
button("Cancel", close_dialog);
});
return resp;
});
}
}
protected void edit_description_dialog(){
if(CraftData.selected_craft.description == null){
CraftData.selected_craft.description = "";
}
string resp = "";
float area_height = 0;
string original_desc = new string(CraftData.selected_craft.description.ToCharArray());
bool show_scroll_label = false;
show_dialog("Edit Description", "", d =>{
GUI.SetNextControlName("dialog_focus_field");
area_height = skin.textArea.CalcHeight(new GUIContent(CraftData.selected_craft.description), d.window_pos.width)+10;
if(area_height < 150f){area_height=150f;}
if(area_height > Screen.height*0.4f){
area_height = Screen.height*0.4f;
show_scroll_label = true;
}
CraftData.selected_craft.description = GUILayout.TextArea(CraftData.selected_craft.description.Replace("¨", "\n"), height(area_height));
section(()=>{
if(show_scroll_label){
label("use up/down arrow keys to scroll content", "small");
}
fspace();
button("Cancel", ()=>{
CraftData.selected_craft.description = original_desc;
close_dialog();
});
resp = submit("Save", CraftData.selected_craft.save_description);
});
return resp;
});
}
protected void edit_action_group_dialog(){
string resp = "";
show_dialog("Edit Action Group info", "Edit Action Group info", 700f, d =>{
CraftData craft = CraftData.selected_craft;
if(craft !=null){
Dictionary<string, string> action_groups = craft.upload_data.action_groups;
List<string> keys = new List<string>(action_groups.Keys);
List<int> counts = new List<int>{ keys.Count, craft.upload_data.action_groups.Count };
float label_width = 45f;
GUI.SetNextControlName("dialog_focus_field");
section(()=>{
for(int j=0; j < counts.Count; j++){
v_section(350f, (w)=>{
for(int i=0; i < counts[j]; i++){
section(()=>{
string key = keys[i];
if(j==0 && key.Length <= 1 || j==1 && key.Length > 1){
label(key, width(label_width));
action_groups[key] = GUILayout.TextField(action_groups[key], width(w-label_width));
}
});
}
});
}
});
}else{
fspace();
label("No craft is selected", "alert.h2");
fspace();
}
section((w)=>{
fspace();
resp = submit("OK", "button.large", ()=>{ return "200";});
});
return resp;
});
}
//Show user option to save current craft or carry on loading
internal void load_craft_confirm_dialog(DialogAction action){
string resp = "";
show_dialog("Confirm Load", "The Current Craft has unsaved changes", d =>{
section((w)=>{
button("Save Current Craft first", "button.continue_with_save", ()=>{
string path = ShipConstruction.GetSavePath(EditorLogic.fetch.ship.shipName);
EditorLogic.fetch.ship.SaveShip().Save(path);
action(); resp = "200";
});
button("Continue Without Saving", "button.continue_no_save", ()=>{
action(); resp = "200";
});
});
GUILayout.Space(10);
button("Cancel", "button.cancel_load", close_dialog);
return resp;
});
}
internal void mod_lookup_dialog(CraftData craft){
string resp = "";
bool lookup_complete = false;
List<string> required_mods = new List<string>();
if(KerbalX.enabled){
KerbalX.lookup_parts(craft.list_parts(), identified_parts =>{
foreach(string mod_name in identified_parts.Values){
if(mod_name != "Squad"){
required_mods.AddUnique(mod_name);
}
}
lookup_complete = true;
});
}
show_dialog("Mod Lookup", "", d =>{
section((w)=>{
if(lookup_complete){
if(required_mods.Count == 0){
label("This craft doesn't have any modded parts");
}else{
label("This craft needs the following mods:\n" + required_mods.n_join("and"));
}
}else{
label("Fetching Mod info from KerbalX", "h2");
}
});
section(()=>{
button("close", close_dialog);
});
return resp;
});
}
internal void load_craft_with_missing_parts_dialog(CraftData craft, string load_type){
string resp = "";
List<string> missing_parts_list = new List<string>();
Dictionary<string, string> identified_parts_list = new Dictionary<string, string>();
bool show_identified_parts = false;
List<string> required_mods = new List<string>();
Vector2 part_list_scroll = new Vector2();
show_dialog("Missing Parts", "This Craft has missing parts", d =>{
section(()=>{
button("list missing parts", ()=>{
show_identified_parts = false;
missing_parts_list = craft.list_missing_parts();
});
if(KerbalX.enabled){
button("KerblaX Mod lookup", () => {
KerbalX.lookup_parts(craft.list_missing_parts(), identified_parts => {
identified_parts_list = identified_parts;
missing_parts_list.Clear();
part_list_scroll = new Vector2();
required_mods.Clear();
foreach(string mod_name in identified_parts_list.Values){
if(mod_name != "Squad"){
required_mods.AddUnique(mod_name);
}
}
show_identified_parts = true;
});
});
}
});
if(KerbalX.enabled){
label("click \"KerbalX Mod Lookup\" and KerbalX will try to work out which mods you need for the missing parts");
}else{
label("tip: if you enable KerbalX integration, Craft Manager can use KerbalX to try and work out which mods you need", "small");
}
if(missing_parts_list.Count > 0){
part_list_scroll= scroll(part_list_scroll, d.window_pos.width, 200, (w) => {
foreach(string part_name in missing_parts_list){
label(part_name);
}
});
}
if(show_identified_parts){
if(identified_parts_list.Count > 0){
part_list_scroll= scroll(part_list_scroll, d.window_pos.width, 200, (w) => {
foreach(KeyValuePair<string, string> pair in identified_parts_list){
section(()=>{
label(pair.Key, "Label", w*0.3f);
label(pair.Value, "Label", w*0.6f);
});
}
});
if(identified_parts_list.ContainsValue("Squad")){
label("You seem to be missing some parts from the core game!", "alert");
}
if(required_mods.Count > 0){
label("This craft needs the following mods:\n" + required_mods.n_join("and"));
}
}else{
label("no part info found");
}
}
section(()=>{
button("Try to load anyway", ()=>{
load_craft(load_type + "_ignore_missing", false);
resp = "200";
});
button("Cancel", close_dialog);
});
return resp;
});
}
//**Tag Dialogs**//
//Call Create Tag Dialog (using tag_dialog_form)
internal void create_tag_dialog(){create_tag_dialog(true, null, true);}
internal void create_tag_dialog(bool show_rule_opts = true, List<CraftData> auto_add_craft = null, bool autopos = false){
float top = this.window_pos.y + (this.window_pos.height*0.4f);
float left = this.window_pos.x + (this.window_pos.width/2) - (200f);
if(autopos){
top = this.window_pos.y + scroll_relative_pos.y + main_section_height - 130f;
left = this.window_pos.x + scroll_relative_pos.x + (col_widths_current[0] * window_width) - 400f;
}
string save_dir_for_tag = active_save_dir;
if(save_dir_for_tag == all_saves_ref){
save_dir_for_tag = current_save_dir;
}
// if(auto_add_craft != null){
// save_dir_for_tag = auto_add_craft[0].save_dir;
// }
tag_dialog_form("Create", show_rule_opts, "", save_dir_for_tag, false, "", "", "", top, left, auto_add_craft);
}
//Edit Tag Dialog (using tag_dialog_form)
protected void edit_tag_dialog(string tag_name, float top, float left){
Tag tag = Tags.find(tag_name, active_save_dir);
tag_dialog_form("Edit", true, tag.name, active_save_dir, tag.rule_based, tag.rule_attribute, tag.rule_comparitor, tag.rule_value, top, left, null);
}
//The main dialog used for both editing and creating tags
protected void tag_dialog_form(string mode, bool show_rule_opts, string tag_name, string save_dir, bool rule_based,
string rule_attr, string rule_comparator, string rule_value, float top, float left, List<CraftData> auto_add_craft = null
){
string initial_name = tag_name;
string resp = "";
string header = (mode == "Create" ? "Create Tag" : ("Edit Tag: " + tag_name));
Rect d_offset = new Rect();
List<Tag> tags = Tags.find_all(tag_name, save_dir);
DropdownMenuData rule_attrs = new DropdownMenuData(Tags.instance.rule_attributes);
DropdownMenuData rule_comparators = new DropdownMenuData();
DropdownMenuData bool_opts = new DropdownMenuData(new List<string>{"True", "False"});
string prev_rule_attr = rule_attr;
string sel_attr_type = "";
bool first_pass = true;
show_dialog("Tag Form", header, top, left, 400f, true, d =>{
d_offset.x = -d.window_pos.x; d_offset.y = -d.window_pos.y;
if(tags.Count > 1){
label("You are viewing craft from all saves, this will edit " + tags.Count + " tags called " + initial_name + " in each of your saves.", "alert.h3");
}
GUI.SetNextControlName("dialog_focus_field");
tag_name = GUILayout.TextField(tag_name, width(400f-22f));
if(show_rule_opts){
rule_based = GUILayout.Toggle(rule_based, "Use Auto Tag rule", "Button");
}
if(rule_based){
section(()=>{
prev_rule_attr = rule_attr;
section(d.window_pos.width*0.35f, ()=>{
dropdown((String.IsNullOrEmpty(rule_attr) ? "Select an Attribute" : rule_attrs.items[rule_attr]), StyleSheet.assets["caret-down"],
"tag_rule_attr_menu", rule_attrs, d, d_offset, d.window_pos.width*0.35f,
(sel_attr)=>{
rule_attr = sel_attr;
}
);
});
if(prev_rule_attr != rule_attr || first_pass){
if(!String.IsNullOrEmpty(rule_attr)){
sel_attr_type = typeof(CraftData).GetProperty(rule_attr).PropertyType.ToString();
if(sel_attr_type == "System.String"){
rule_comparators.set_data(new Dictionary<string, string>(Tags.instance.rule_comparitors_string));
}else if(sel_attr_type == "System.Int32" || sel_attr_type == "System.Single"){
rule_comparators.set_data(new Dictionary<string, string>(Tags.instance.rule_comparitors_numeric));
}
}
}
if(!rule_comparators.items.ContainsKey(rule_comparator)){
rule_comparator = "equal_to";
}
if(sel_attr_type == "System.Int32" || sel_attr_type == "System.Single"){
rule_value = System.Text.RegularExpressions.Regex.Replace(rule_value, "[^0-9]", "");
}
if(!String.IsNullOrEmpty(sel_attr_type)){
if(sel_attr_type == "System.Boolean"){
if(!bool_opts.items.ContainsKey(rule_value)){
rule_value = "True";
}
rule_comparator = "equal_to";
label("==", "Button", width(d.window_pos.width*0.25f));
dropdown(rule_value, StyleSheet.assets["caret-down"], "tag_rule_bool_opt_menu", bool_opts, d, d_offset, d.window_pos.width*0.25f, (bool_val)=>{
rule_value = bool_val;
});
}else{
section(d.window_pos.width*0.25f, ()=>{
dropdown(rule_comparators.items[rule_comparator], StyleSheet.assets["caret-down"], "tag_rule_comp_menu", rule_comparators, d, d_offset, d.window_pos.width*0.25f, (sel_comparator)=>{
rule_comparator = sel_comparator;
});
});
rule_value = GUILayout.TextField(rule_value);
}
}
});
}
section((w)=>{
fspace();
button("Cancel", close_dialog);
if(mode == "Edit"){
resp = submit("Update", ()=>{
return Tags.update(initial_name, tag_name, save_dir, rule_based, rule_attr, rule_comparator, rule_value);
});
}else{
resp = submit("Create Tag", ()=>{
return Tags.create(tag_name, save_dir, rule_based, rule_attr, rule_comparator, rule_value, auto_add_craft);
});
}
});
return resp;
});
}
protected void delete_tag_dialog(string tag_name, float top, float left){
string resp = "";
int craft_count = CraftData.cache.tag_craft_count_for(tag_name);
show_dialog("Delete Tag", "Are you sure you want to delete this tag?", top, left, 400f, true, d =>{
if(active_save_dir == all_saves_ref){
label("You are viewing craft from all saves, this tag will be deleted in each of your saves.", "alert.h3");
}
if(craft_count > 0){
label("This tag is used by " + craft_count + " craft.");
label("deleting tags will not delete any craft", "compact");
}
section(()=>{
fspace();
button("Cancel", close_dialog);
resp = submit("Delete", "button.delete", ()=>{
return Tags.remove(tag_name, active_save_dir);
});
});
return resp;
});
}
//**KerbalX Specific Dialogs**//
internal void download_confirm_dialog(DialogAction action = null){
string resp = "";
show_dialog("Overwrite", "A Craft with this name already exists", d =>{
section(()=>{
button("Replace Existing Craft", "button.continue_with_save", ()=>{
download(true, true, action);
resp = "200";
});
button("Load Craft (without saving)", "button.continue_no_save", ()=>{
load_craft("dl_load_no_save");
resp = "200";
});
});
GUILayout.Space(10);
button("Cancel", "button.cancel_load", close_dialog);
return resp;
});
}
public void upload_complete_dialog(int remote_status_code, SimpleJSON.JSONNode response){
show_dialog(remote_status_code == 200 ? "Upload Complete" : "Upload Error", "", d =>{
if(remote_status_code == 200){
string craft_url = response["url"];
string craft_full_url = KerbalX.api.url_to(craft_url);
label("Your Craft has been uploaded!", "h2");
button("It is now available here:\n" + craft_full_url, "hyperlink.bold", ()=>{
Application.OpenURL(craft_full_url);
close_dialog();
});
button(StyleSheet.assets["logo_large"], "centered", 664, 120, ()=> {
Application.OpenURL(craft_full_url);
close_dialog();
});
section(()=>{
fspace();
label("click the url or the logo to see your craft on KerbalX", "compact");
});
}else{
label("There was a problem uploading your craft. Error code " + remote_status_code, "h3");
label(response); //TODO: make this nicer.
}
section(()=>{
fspace();
button("close", close_dialog);
});
return "";
});
}
protected void populate_new_save_dialog(){
KerbalX.get_craft_ids_by_version((craft_by_version, versions) =>{
string v1 = versions[0].ToString();
string v2 = versions[1].ToString();
string resp = "";
Version ksp_version = CraftManager.game_version; //new Version(Versioning.GetVersionString());
KerbalX.log_scroll = new Vector2();
KerbalX.bulk_download_log = "";
show_dialog("Import Craft From KerbalX", "", d =>{
label("You don't have any craft in this save yet.", "h2");
label("Do you want to fetch your craft from KerbalX?", "h2");
if(versions[0] == ksp_version){
button("download " + craft_by_version[v1].Count + " craft built in KSP " + ksp_version, ()=>{
KerbalX.bulk_download(craft_by_version[v1], current_save_dir, ()=>{});
});
}else{
label("You don't have any craft made in this version of KSP");
if(v1 != null || v2 != null){
label("get craft from previous versions:");
section(()=>{
if(v1 != null && craft_by_version[v1] != null ){
button("download " + craft_by_version[v1].Count + " craft built in KSP " + v1, ()=>{
KerbalX.bulk_download(craft_by_version[v1], current_save_dir, ()=>{});
});
}
if(v2 != null && craft_by_version[v2] != null ){
button("download " + craft_by_version[v2].Count + " craft built in KSP " + v2, ()=>{
KerbalX.bulk_download(craft_by_version[v2], current_save_dir, ()=>{});
});
}
});
}
}
if(!String.IsNullOrEmpty(KerbalX.bulk_download_log)){
KerbalX.log_scroll = scroll(KerbalX.log_scroll, d.window_pos.width, 80f, (w)=>{
label(KerbalX.bulk_download_log);
});
}
button("OR cherry pick the ones you want", ()=>{
close_dialog();
exit_kerbalx_mode_after_close = true;
CraftManager.main_ui.show();
KerbalX.load_remote_craft();
});
button("Close", close_dialog);
return resp;
});
});
}
protected void show_bulk_download_dialog(){
string resp = "";
long time_completed = 0;
long close_delay = 3000;
long time_to_close = close_delay;
Dictionary<int, Dictionary<string, string>> ids_to_download = new Dictionary<int, Dictionary<string, string>>();
foreach(CraftData craft in CraftData.selected_group){
if(!ids_to_download.ContainsKey(craft.remote_id)){
ids_to_download.Add(craft.remote_id, new Dictionary<string, string>{{"name", craft.name}, {"type", craft.construction_type}});
}
}
KerbalX.bulk_download_log = "";
KerbalX.bulk_download(ids_to_download, current_save_dir, ()=>{
time_completed = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
});
KerbalX.log_scroll = new Vector2();
show_dialog("Downloading from KerbalX...", "", d =>{
if(!String.IsNullOrEmpty(KerbalX.bulk_download_log)){
KerbalX.log_scroll = scroll(KerbalX.log_scroll, d.window_pos.width, 80f, (w)=>{
label(KerbalX.bulk_download_log);
});
}
if(time_completed != 0){
time_to_close = close_delay - ((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - time_completed);
section(()=>{
fspace();
button("close (auto closing in " + ((time_to_close / (TimeSpan.TicksPerMillisecond/10)) + 1).ToString() + ")", close_dialog);
});
if( time_to_close <= 0){
resp = "200";
}
}
return resp;
});
}
internal void close_update_kerbalx_craft_dialog(){
close_dialog();
}
protected void show_update_kerbalx_craft_dialog(){
string resp = "";
CraftData craft = CraftData.selected_craft;
if(craft.upload_data == null){ //create new instance of upload data if it's not already been set
craft.upload_data = KerbalXUploadData.prepare_for(craft);
}
craft.upload_data.update_to_id = craft.matching_remote_ids.Count == 0 ? 0 : craft.matching_remote_ids[0];
string menu_label = "";
string kx_url = "";
bool show_craft_select_menu = craft.matching_remote_ids.Count != 1;
Rect d_offset = new Rect();
Dictionary<string, string> upload_select_data_matching = new Dictionary<string, string>();
Dictionary<string, string> upload_select_data_all = new Dictionary<string, string>();
foreach(int id in craft.matching_remote_ids){
upload_select_data_matching.Add(id.ToString(), KerbalX.api.user_craft[id]["url"].Replace(("/" + KerbalX.api.logged_in_as+ "/"), ""));
}
foreach(KeyValuePair<int, Dictionary<string, string>> c in KerbalX.api.user_craft){
upload_select_data_all.Add(c.Key.ToString(), c.Value["url"].Replace(("/" + KerbalX.api.logged_in_as+ "/"), ""));
}
DropdownMenuData upload_select_menu = new DropdownMenuData();
DropdownMenuData upload_select_menu_2 = new DropdownMenuData();
if(craft.matching_remote_ids.Count == 0){
upload_select_menu.set_data(upload_select_data_all);
} else{
upload_select_menu.set_data(upload_select_data_matching);
}
upload_select_menu_2.set_data(upload_select_data_all);
show_dialog("Update KerbalX Craft", "", d =>{
d_offset.x = -d.window_pos.x; d_offset.y = -d.window_pos.y;
if(craft.upload_data.update_to_id != 0){
menu_label = KerbalX.api.user_craft[craft.upload_data.update_to_id]["url"].Replace(("/" + KerbalX.api.logged_in_as + "/"), "");
kx_url = KerbalX.api.url_to(KerbalX.api.user_craft[craft.upload_data.update_to_id]["url"]);
}else{
menu_label = "Select a craft";
}
if(show_craft_select_menu){
if(craft.matching_remote_ids.Count > 1){
label("There are " + craft.matching_remote_ids.Count + " craft in your account with the same name as this craft");
}
label("Select which craft you want to update");
dropdown(menu_label, StyleSheet.assets["caret-down"], "upload_select_menu", upload_select_menu, d, d_offset, d.window_pos.width*0.8f, (sel)=>{
craft.upload_data.update_to_id = int.Parse(sel);
});
if(upload_select_menu.items != upload_select_data_all){
section(()=>{
fspace();
button("show all your craft in menu", "hyperlink", ()=>{
upload_select_menu.set_data(upload_select_data_all);
open_dropdown(upload_select_menu);
});
});
}
}
if(!String.IsNullOrEmpty(kx_url)){
label("This will update the KerbalX craft:");
section((w)=>{
button(kx_url, "hyperlink.update_url", 500f, 800f, ()=>{ Application.OpenURL(kx_url); });
if(!show_craft_select_menu){
dropdown("edit", StyleSheet.assets["caret-down"], "upload_select_menu_2", upload_select_menu_2, d, d_offset, d.window_pos.width*0.8f, (sel)=>{
craft.upload_data.update_to_id = int.Parse(sel);
});
}
});
section(()=>{
fspace();
label("make sure this is the craft you want to update!", "small.compact");
});
}
foreach(string error in craft.upload_data.errors){
label(error, "error.bold");
}
GUILayout.Space(10f);
gui_state(craft.upload_data.update_to_id > 0, ()=>{
button("Confirm Update", "button.large", ()=>{
craft.upload_data.put();
});
});
GUILayout.Space(4f);
section(()=>{
button("Upload as a new Craft", "button.large", open_upload_interface);
button("Cancel", "button.large", close_dialog);
});
return resp;
});
}
//Dialog Handler
//All the above dialogs are created with this function and it handles all their common aspects and cam render them as modal dialogs.
//It's a bit of a hacky solution. Its renders a full screen size window with GUI.ModalWindow which is skinned to appear as a box,
//and then renders a box which is skinned to look like a window in the middle. So the effect is a shaded out screen with a dialog
//in the middle and only the dialog can be interacted with.
public delegate string InnerDialogContent(DryUI dialog);
protected DryDialog show_dialog(string title, string heading, InnerDialogContent content){
return show_dialog(title, heading, Screen.height / 3, this.window_pos.x + (this.window_pos.width / 2) - (500 / 2), 500f, true, content);
}
protected DryDialog show_dialog(string title, string heading, bool modal, InnerDialogContent content){
return show_dialog(title, heading, Screen.height / 3, this.window_pos.x + (this.window_pos.width / 2) - (500 / 2), 500f, modal, content);
}
protected DryDialog show_dialog(string title, string heading, float dialog_width, InnerDialogContent content){
return show_dialog(title, heading, Screen.height / 3, this.window_pos.x + (this.window_pos.width / 2) - (dialog_width / 2), dialog_width, true, content);
}
protected DryDialog show_dialog(string title, string heading, float top, float left, float dialog_width, InnerDialogContent content){
return show_dialog(title, heading, top, left, dialog_width, false, content);
}
protected DryDialog show_dialog(string title, string heading, float top, float left, float dialog_width, bool modal, InnerDialogContent content){
close_dialog();
string response = "";
string err_msg = "";
int focus_count = 5;
//wrapper for the given content which adds some of the common functionality
DialogContent dc = new DialogContent(d =>{
//close on escape key press
Event e = Event.current;
if (e.type == EventType.KeyDown && e.keyCode == KeyCode.Escape) {
close_dialog();
}
//main dialog
style_override = "dialog.section";
v_section(()=>{
if(!String.IsNullOrEmpty(heading)){
label(heading, "h2");
}
if(!String.IsNullOrEmpty(err_msg)){label(err_msg, "error");} //display error message if any
response = content(d); //render main dialog content which will return a response string.
if(!String.IsNullOrEmpty(response) && response != "200"){ //collect any error message returned (as next pass response will be empty again).
err_msg = response;
}
});
//autofocus on textfield/area - the reason for the focous_count countdown is because we only want to focus on the field just after creating the dialog
//but one the first (few) passes it doesn't appear to be ready, so this slightly hacky solution keeps setting the focus for first 5 passes.
if(focus_count > 0){
auto_focus_field = "dialog_focus_field";
focus_count--;
}
//close dialog on OK response
if(response == "200"){
close_dialog();
}
});
if(modal){
ModalDialog dialog = gameObject.AddOrGetComponent<ModalDialog>();
dialog.dialog_pos = new Rect(left, top, dialog_width, 80f);
dialog.window_title = title;
dialog.content = dc;
dialog.skin = CraftManager.skin;
return dialog;
} else{
DryDialog dialog = gameObject.AddOrGetComponent<DryDialog>();
dialog.window_pos = new Rect(left, top, dialog_width, 80f);
dialog.window_title = title;
dialog.content = dc;
dialog.skin = CraftManager.skin;
return dialog;
}
}
//Submit Button Helper
//string response = submit("text", "style", ()=>{ return shit_that_happens_on_submit })
//creates a button which performs whatever actions are describe in the delegate passed to the function
//and also sets up detection of enter key press which will call the same actions.
bool submit_clicked = false;
protected delegate string SubmitAction();
protected string submit(string button_label, SubmitAction submit_action){
return submit(button_label, "Button", submit_action);
}
protected string submit(string button_label, GUIStyle button_style, SubmitAction submit_action){
submit_clicked = false;
button(button_label, button_style, () =>{
submit_clicked = true;
});
Event e = Event.current;
if (GUI.GetNameOfFocusedControl() == "dialog_focus_field" && e.type == EventType.KeyDown && (e.keyCode == KeyCode.Return || e.keyCode == KeyCode.KeypadEnter)) {
submit_clicked = true;
e.Use();
}
if(submit_clicked){
return submit_action();
} else{
return "";
}
}
}
}