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 }