1 module nodes.popup_button; 2 3 import fluid; 4 import fluid.future.pipe; 5 6 @safe: 7 8 @("PopupButton supports keyboard navigation") 9 unittest { 10 11 import fluid.backend; 12 13 string lastAction; 14 15 void action(string text)() { 16 lastAction = text; 17 } 18 19 Button[6] buttons; 20 21 auto main = popupButton("Options", 22 buttons[0] = button("Edit", &action!"edit"), 23 buttons[1] = button("Copy", &action!"copy"), 24 buttons[2] = popupButton("Share", 25 buttons[3] = button("SMS", &action!"sms"), 26 buttons[4] = button("Via e-mail", &action!"email"), 27 buttons[5] = button("Send to device", &action!"device"), 28 ), 29 ); 30 auto focus = focusChain(nullTheme, main); 31 auto overlay = overlayChain(focus); 32 auto root = overlay; 33 34 auto sharePopupButton = cast(PopupButton) buttons[2]; 35 auto sharePopup = sharePopupButton.popup; 36 37 root.draw(); 38 39 // Focus the button 40 focus.focusNext() 41 .thenAssertEquals(main) 42 43 // Press it 44 .then(() => focus.runInputAction!(FluidInputAction.press)) 45 .then(_ => root.nextFrame) 46 47 // A popup should open 48 .then(() => assert(focus.isFocused(buttons[0]), "The first button inside should be focused")) 49 50 // Go to the previous button, expecting wrap 51 .then(() => focus.runInputAction!(FluidInputAction.focusPrevious)) 52 .then(_ => root.nextFrame) 53 .then(() => assert(focus.isFocused(buttons[2]), "The last button inside the first menu should be focused")) 54 55 // Press the last button 56 .then(() => buttons[2].press()) 57 .then(() => root.nextFrame) 58 .then(() => assert(focus.isFocused(buttons[3]), "The first button of the second menu should be focused")) 59 60 // The up arrow should do nothing 61 .then(() => focus.runInputAction!(FluidInputAction.focusUp)) 62 .then(_ => root.nextFrame) 63 .then(() => assert(focus.isFocused(buttons[3]))) 64 65 // Press the down arrow 66 .then(() => focus.runInputAction!(FluidInputAction.focusDown)) 67 .then(_ => root.nextFrame) 68 .then(() => assert(focus.isFocused(buttons[4]))) 69 70 // Press the button 71 .then(() => buttons[4].press) 72 .then(() => assert(focus.isFocused(buttons[4]))) 73 .then(() => assert(lastAction == "email")) 74 .then(() => assert(!sharePopup.isHidden)) 75 76 // Close the popup 77 .then(() => focus.runInputAction!(FluidInputAction.cancel)) 78 79 // Need two frames to process the tree action 80 .then(_ => main.nextFrame) 81 .then(() => main.nextFrame) 82 83 // Need another frame for the tree action 84 .then(() => assert(sharePopup.isHidden)); 85 86 } 87 88 @("PopupButton uses OverlayIO to create the popup") 89 unittest { 90 91 auto button = popupButton("Hello", 92 label("Popup opened"), 93 ); 94 auto overlay = overlayChain( 95 .layout!(1, "fill"), 96 button 97 ); 98 auto root = sizeLock!testSpace( 99 .nullTheme, 100 .sizeLimit(400, 400), 101 overlay 102 ); 103 104 root.drawAndAssert(button.isDrawn); 105 root.drawAndAssertFailure(button.popup.isDrawn); 106 107 button.press(); 108 109 root.drawAndAssert( 110 button.isDrawn, 111 button.popup.isDrawn.at(button.getMinSize), 112 ); 113 root.drawAndAssert( 114 overlay.drawsChild(button.popup), 115 ); 116 117 } 118 119 @("PopupButton works with hover input") 120 unittest { 121 122 int onePressed, twoPressed; 123 124 auto btn2 = 125 popupButton( 126 "Second level", 127 button("two", delegate { twoPressed++; }), 128 ); 129 auto btn = 130 sizeLock!popupButton( 131 .sizeLimit(250, 250), 132 "First level", 133 button("one", delegate { onePressed++; }), 134 btn2, 135 ); 136 auto overlay = overlayChain(); 137 auto hover = hoverChain(); 138 auto focus = focusChain(); 139 auto root = testSpace( 140 .nullTheme, 141 chain(focus, hover, overlay, btn), 142 ); 143 144 root.drawAndAssertFailure( 145 overlay.drawsChild(btn.popup), 146 ); 147 148 hover.point(100, 100) 149 .then((a) { 150 a.click; 151 return a.stayIdle; 152 }) 153 .runWhileDrawing(root, 2); 154 root.drawAndAssert( 155 overlay.drawsChild(btn.popup), 156 ); 157 root.drawAndAssertFailure( 158 overlay.drawsChild(btn2.popup), 159 ); 160 focus.findFocusBox() 161 .then((box) { 162 return hover.point(box.front.center); 163 }) 164 .then((a) { 165 a.click; 166 return a.stayIdle; 167 }) 168 .runWhileDrawing(root, 3); 169 root.drawAndAssert( 170 overlay.drawsChild(btn.popup), 171 ); 172 root.drawAndAssert( 173 overlay.drawsChild(btn2.popup), 174 ); 175 176 }