1 /// 2 module dli.index_menu; 3 4 import dli.text_menu; 5 import dli.exceptions; 6 import std.conv; 7 import std.exception; 8 import std.stdio; 9 import std.typecons : Tuple, tuple; 10 11 /// 12 public class IndexMenu(inputStreamT, outputStreamT) : TextMenu!(inputStreamT, outputStreamT, size_t) 13 { 14 private size_t highestMenuItemIndex; 15 16 /// 17 public this(inputStreamT inputStream, outputStreamT outputStream) 18 { 19 super(inputStream, outputStream); 20 } 21 22 /// 23 public override void addItem(MenuItem item, size_t key) 24 { 25 super.addItem(item, key); 26 27 import std.algorithm.comparison : max; 28 29 highestMenuItemIndex = max(highestMenuItemIndex, key); 30 } 31 32 /// Adds the given item, automatically assigning the next highest available key 33 public void addItem(MenuItem item) 34 { 35 addItem(item, highestMenuItemIndex + 1); 36 } 37 38 protected override void addExitMenuItem(MenuItem exitMenuItem) 39 { 40 menuItems[highestMenuItemIndex + 1] = exitMenuItem; 41 } 42 43 protected override void removeExitMenuItem() 44 { 45 menuItems.remove(highestMenuItemIndex + 1); 46 } 47 48 protected override Tuple!(size_t, MenuItem)[] sortItemsForDisplay() 49 { 50 // It is important that the items are printed in the correct order 51 import std.algorithm.sorting : sort; 52 auto sortedKeys = sort(menuItems.keys); 53 54 Tuple!(size_t, MenuItem)[] sortedItems; 55 56 foreach(size_t menuItemIndex; sortedKeys) 57 sortedItems ~= tuple(menuItemIndex, menuItems[menuItemIndex]); 58 59 return sortedItems; 60 } 61 } 62 63 /// 64 public auto createIndexMenu(File inStream = stdin, File outStream = stdout) 65 { 66 return new IndexMenu!(File, File)(inStream, outStream); 67 } 68 69 /// 70 public auto createIndexMenu(inputStreamT, outputStreamT)(inputStreamT inStream, outputStreamT outStream) 71 { 72 return new IndexMenu!(inputStreamT, outputStreamT)(inStream, outStream); 73 } 74 75 // TESTS 76 version(unittest) 77 { 78 import dli.string_stream.input_string_stream; 79 import dli.string_stream.output_string_stream; 80 import test.dli.mock_menu_item; 81 82 @("Test IndexMenu.addItem(MenuItem) properly places items") 83 unittest 84 { 85 auto inputStream = new shared InputStringStream(); 86 auto menu = createIndexMenu(inputStream, new shared OutputStringStream()); 87 auto item1 = new MockMenuItem(); 88 auto item2 = new MockMenuItem(); 89 // We deliberately skip item 3 90 auto item4 = new MockMenuItem(); 91 auto item5 = new MockMenuItem(); 92 93 menu.addItem(item1); 94 menu.addItem(item2); 95 menu.addItem(item4, 4); // This one we specify the key 96 menu.addItem(item5); 97 98 inputStream.appendLine("1"); 99 inputStream.appendLine("2"); 100 inputStream.appendLine("6"); // 6 should correspond to the exit menu item 101 menu.run(); 102 103 assert(item1.executed); 104 assert(item2.executed); 105 assert(!item4.executed); 106 assert(!item5.executed); 107 108 inputStream.appendLine("4"); 109 inputStream.appendLine("5"); 110 inputStream.appendLine("6"); 111 menu.run(); 112 113 assert(item4.executed); 114 assert(item5.executed); 115 } 116 }