1 /// 2 module fluid.popup_button; 3 4 import fluid.node; 5 import fluid.utils; 6 import fluid.label; 7 import fluid.style; 8 import fluid.types; 9 import fluid.button; 10 import fluid.popup_frame; 11 12 import fluid.io.overlay; 13 14 @safe: 15 16 /// A button made to open popups. 17 alias popupButton = simpleConstructor!PopupButton; 18 19 // For no known reason, this will not compile (producing the most misleading error of the century) if extending directly 20 // from Button. 21 22 /// ditto 23 class PopupButton : ButtonImpl!Label { 24 25 mixin enableInputActions; 26 27 OverlayIO overlayIO; 28 29 public { 30 31 /// Popup enabled by this button. 32 PopupFrame popup; 33 34 /// Popup this button belongs to, if any. Set automatically if the popup is spawned with `spawnPopup`. 35 /// 36 /// This field will be removed in Fluid 0.8.0. 37 PopupFrame parentPopup; 38 39 } 40 41 private { 42 43 Rectangle _inner; 44 45 // workaround for https://git.samerion.com/Samerion/Fluid/issues/401 46 // could be fixed with https://git.samerion.com/Samerion/Fluid/issues/399 47 bool _justOpened; 48 49 } 50 51 /// Create a new button. 52 /// Params: 53 /// text = Text for the button. 54 /// popupChildren = Children to appear within the button. 55 this(string text, Node[] popupChildren...) { 56 57 // Craft the popup 58 popup = popupFrame(popupChildren); 59 60 super(text, delegate { 61 62 // New I/O 63 if (overlayIO) { 64 65 const anchor = focusBoxImpl(_inner); 66 67 // Parent popup active 68 if (parentPopup && parentPopup.isFocused) 69 overlayIO.addChildPopup(parentPopup, popup, anchor); 70 71 // No parent 72 else { 73 overlayIO.addPopup(popup, anchor); 74 } 75 76 _justOpened = true; 77 78 } 79 80 // Parent popup active 81 else if (parentPopup && parentPopup.isFocused) 82 parentPopup.spawnChildPopup(popup); 83 84 // No parent 85 else { 86 popup.theme = theme; 87 tree.spawnPopup(popup); 88 } 89 90 }); 91 92 } 93 94 override void resizeImpl(Vector2 space) { 95 use(overlayIO); 96 super.resizeImpl(space); 97 } 98 99 override void drawImpl(Rectangle outer, Rectangle inner) { 100 _inner = inner; 101 super.drawImpl(outer, inner); 102 if (hoverIO && !hoverIO.isHovered(this)) { 103 _justOpened = false; 104 } 105 } 106 107 override void focus() { 108 if (hoverIO && _justOpened) return; 109 super.focus(); 110 } 111 112 override string toString() const { 113 114 import std.format; 115 return format!"popupButton(%s)"(text); 116 117 } 118 119 } 120 121 /// 122 unittest { 123 124 auto myButton = popupButton("Options", 125 button("Edit", delegate { }), 126 button("Copy", delegate { }), 127 popupButton("Share", 128 button("SMS", delegate { }), 129 button("Via e-mail", delegate { }), 130 button("Send to device", delegate { }), 131 ), 132 ); 133 134 }